home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / fw_xemacs.idb / usr / freeware / lib / xemacs-20.4 / lisp / vm / vm-mime.el.z / vm-mime.el
Encoding:
Text File  |  1998-05-21  |  125.3 KB  |  3,557 lines

  1. ;;; MIME support functions
  2. ;;; Copyright (C) 1997-1998 Kyle E. Jones
  3. ;;;
  4. ;;; This program is free software; you can redistribute it and/or modify
  5. ;;; it under the terms of the GNU General Public License as published by
  6. ;;; the Free Software Foundation; either version 1, or (at your option)
  7. ;;; any later version.
  8. ;;;
  9. ;;; This program is distributed in the hope that it will be useful,
  10. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. ;;; GNU General Public License for more details.
  13. ;;;
  14. ;;; You should have received a copy of the GNU General Public License
  15. ;;; along with this program; if not, write to the Free Software
  16. ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. (provide 'vm-mime)
  19.  
  20. (defun vm-mime-error (&rest args)
  21.   (signal 'vm-mime-error (list (apply 'format args)))
  22.   (error "can't return from vm-mime-error"))
  23.  
  24. (if (fboundp 'define-error)
  25.     (define-error 'vm-mime-error "MIME error")
  26.   (put 'vm-mime-error 'error-conditions '(vm-mime-error error))
  27.   (put 'vm-mime-error 'error-message "MIME error"))
  28.  
  29. (defun vm-mm-layout-type (e) (aref e 0))
  30. (defun vm-mm-layout-qtype (e) (aref e 1))
  31. (defun vm-mm-layout-encoding (e) (aref e 2))
  32. (defun vm-mm-layout-id (e) (aref e 3))
  33. (defun vm-mm-layout-description (e) (aref e 4))
  34. (defun vm-mm-layout-disposition (e) (aref e 5))
  35. (defun vm-mm-layout-qdisposition (e) (aref e 6))
  36. (defun vm-mm-layout-header-start (e) (aref e 7))
  37. (defun vm-mm-layout-body-start (e) (aref e 8))
  38. (defun vm-mm-layout-body-end (e) (aref e 9))
  39. (defun vm-mm-layout-parts (e) (aref e 10))
  40. (defun vm-mm-layout-cache (e) (aref e 11))
  41. ;; if display of MIME part fails, error string will be here.
  42. (defun vm-mm-layout-display-error (e) (aref e 12))
  43.  
  44. (defun vm-set-mm-layout-type (e type) (aset e 0 type))
  45. (defun vm-set-mm-layout-cache (e c) (aset e 11 c))
  46. (defun vm-set-mm-layout-display-error (e c) (aset e 12 c))
  47.  
  48. (defun vm-mm-layout (m)
  49.   (or (vm-mime-layout-of m)
  50.       (progn (vm-set-mime-layout-of
  51.           m
  52.           (condition-case data
  53.           (vm-mime-parse-entity m)
  54.         (vm-mime-error (message "%s" (car (cdr data))))))
  55.          (vm-mime-layout-of m))))
  56.  
  57. (defun vm-mm-encoded-header (m)
  58.   (or (vm-mime-encoded-header-flag-of m)
  59.       (progn (setq m (vm-real-message-of m))
  60.          (vm-set-mime-encoded-header-flag-of
  61.           m
  62.           (save-excursion
  63.         (set-buffer (vm-buffer-of m))
  64.         (save-excursion
  65.           (save-restriction
  66.             (widen)
  67.             (goto-char (vm-headers-of m))
  68.             (or (re-search-forward vm-mime-encoded-word-regexp
  69.                        (vm-text-of m) t)
  70.             'none)))))
  71.          (vm-mime-encoded-header-flag-of m))))
  72.  
  73. (defun vm-mime-Q-decode-region (start end)
  74.   (let ((buffer-read-only nil))
  75.     (subst-char-in-region start end ?_ (string-to-char " ") t)
  76.     (vm-mime-qp-decode-region start end)))
  77.  
  78. (fset 'vm-mime-B-decode-region 'vm-mime-base64-decode-region)
  79.  
  80. (defun vm-mime-Q-encode-region (start end)
  81.   (let ((buffer-read-only nil))
  82.     (subst-char-in-region start end (string-to-char " ") ?_ t)
  83.     (vm-mime-qp-encode-region start end t)))
  84.  
  85. (defun vm-mime-B-encode-region (start end)
  86.   (vm-mime-base64-encode-region start end nil t))
  87.  
  88. (defun vm-mime-crlf-to-lf-region (start end)
  89.   (let ((buffer-read-only nil))
  90.     (save-excursion
  91.       (save-restriction
  92.     (narrow-to-region start end)
  93.     (goto-char start)
  94.     (while (search-forward "\r\n" nil t)
  95.       (delete-char -2)
  96.       (insert "\n"))))))
  97.       
  98. (defun vm-mime-lf-to-crlf-region (start end)
  99.   (let ((buffer-read-only nil))
  100.     (save-excursion
  101.       (save-restriction
  102.     (narrow-to-region start end)
  103.     (goto-char start)
  104.     (while (search-forward "\n" nil t)
  105.       (delete-char -1)
  106.       (insert "\r\n"))))))
  107.       
  108. (defun vm-mime-charset-decode-region (charset start end)
  109.   (or (markerp end) (setq end (vm-marker end)))
  110.   (cond ((or vm-xemacs-mule-p vm-fsfemacs-mule-p)
  111.      (if (or (and vm-xemacs-p (eq (device-type) 'x))
  112.          (and vm-fsfemacs-p (eq window-system 'x))
  113.          nil)
  114.          (let ((buffer-read-only nil)
  115.            (cell (cdr (vm-string-assoc
  116.                    charset
  117.                    vm-mime-mule-charset-to-coding-alist)))
  118.            (oend (marker-position end))
  119.            (opoint (point)))
  120.            (if cell
  121.            (progn
  122.              (set-marker end (+ start
  123.                     (or (decode-coding-region
  124.                          start end (car cell))
  125.                         (- oend start))))
  126.              (put-text-property start end 'vm-string t)
  127.              (put-text-property start end 'vm-charset charset)
  128.              (put-text-property start end 'vm-coding (car cell))))
  129.            ;; In XEmacs 20.0 beta93 decode-coding-region moves point.
  130.            (goto-char opoint))))
  131.     ((not (vm-multiple-fonts-possible-p)) nil)
  132.     ((vm-string-member charset vm-mime-default-face-charsets) nil)
  133.     (t
  134.      (let ((font (cdr (vm-string-assoc
  135.                charset
  136.                vm-mime-charset-font-alist)))
  137.            (face (make-face (make-symbol "temp-face")))
  138.            (e (vm-make-extent start end)))
  139.        (put-text-property start end 'vm-string t)
  140.        (put-text-property start end 'vm-charset charset)
  141.        (if font
  142.            (condition-case data
  143.            (progn (set-face-font face font)
  144.               (vm-set-extent-property e 'face face))
  145.          (error nil)))))))
  146.  
  147. (defun vm-mime-transfer-decode-region (layout start end)
  148.   (let ((case-fold-search t) (crlf nil))
  149.     (cond ((string-match "^base64$" (vm-mm-layout-encoding layout))
  150.        (cond ((vm-mime-types-match "text"
  151.                        (car (vm-mm-layout-type layout)))
  152.           (setq crlf t))
  153.          ((vm-mime-types-match "message"
  154.                        (car (vm-mm-layout-type layout)))
  155.           (setq crlf t)))
  156.        (vm-mime-base64-decode-region start end crlf))
  157.       ((string-match "^quoted-printable$"
  158.              (vm-mm-layout-encoding layout))
  159.        (vm-mime-qp-decode-region start end)))))
  160.  
  161. (defun vm-mime-base64-decode-region (start end &optional crlf)
  162.   (message "Decoding base64...")
  163.   (let ((work-buffer nil)
  164.     (done nil)
  165.     (counter 0)
  166.     (bits 0)
  167.     (lim 0) inputpos
  168.     (non-data-chars (concat "^=" vm-mime-base64-alphabet)))
  169.     (unwind-protect
  170.     (save-excursion
  171.       (setq work-buffer (generate-new-buffer " *vm-work*"))
  172.       (buffer-disable-undo work-buffer)
  173.       (if vm-mime-base64-decoder-program
  174.           (let* ((binary-process-output t) ; any text already has CRLFs
  175.              (status (apply 'vm-run-command-on-region
  176.                    start end work-buffer
  177.                    vm-mime-base64-decoder-program
  178.                    vm-mime-base64-decoder-switches)))
  179.         (if (not (eq status t))
  180.             (vm-mime-error "%s" (cdr status))))
  181.         (goto-char start)
  182.         (skip-chars-forward non-data-chars end)
  183.         (while (not done)
  184.           (setq inputpos (point))
  185.           (cond
  186.            ((> (skip-chars-forward vm-mime-base64-alphabet end) 0)
  187.         (setq lim (point))
  188.         (while (< inputpos lim)
  189.           (setq bits (+ bits 
  190.                 (aref vm-mime-base64-alphabet-decoding-vector
  191.                       (char-after inputpos))))
  192.           (vm-increment counter)
  193.           (vm-increment inputpos)
  194.           (cond ((= counter 4)
  195.              (vm-insert-char (lsh bits -16) 1 nil work-buffer)
  196.              (vm-insert-char (logand (lsh bits -8) 255) 1 nil
  197.                      work-buffer)
  198.              (vm-insert-char (logand bits 255) 1 nil work-buffer)
  199.              (setq bits 0 counter 0))
  200.             (t (setq bits (lsh bits 6)))))))
  201.           (cond
  202.            ((= (point) end)
  203.         (if (not (zerop counter))
  204.             (vm-mime-error "at least %d bits missing at end of base64 encoding"
  205.                    (* (- 4 counter) 6)))
  206.         (setq done t))
  207.            ((= (char-after (point)) 61) ; 61 is ASCII equals
  208.         (setq done t)
  209.         (cond ((= counter 1)
  210.                (vm-mime-error "at least 2 bits missing at end of base64 encoding"))
  211.               ((= counter 2)
  212.                (vm-insert-char (lsh bits -10) 1 nil work-buffer))
  213.               ((= counter 3)
  214.                (vm-insert-char (lsh bits -16) 1 nil work-buffer)
  215.                (vm-insert-char (logand (lsh bits -8) 255)
  216.                        1 nil work-buffer))
  217.               ((= counter 0) t)))
  218.            (t (skip-chars-forward non-data-chars end)))))
  219.       (and crlf
  220.            (save-excursion
  221.          (set-buffer work-buffer)
  222.          (vm-mime-crlf-to-lf-region (point-min) (point-max))))
  223.       (or (markerp end) (setq end (vm-marker end)))
  224.       (goto-char start)
  225.       (insert-buffer-substring work-buffer)
  226.       (delete-region (point) end))
  227.       (and work-buffer (kill-buffer work-buffer))))
  228.   (message "Decoding base64... done"))
  229.  
  230. (defun vm-mime-base64-encode-region (start end &optional crlf B-encoding)
  231.   (and (> (- end start) 200)
  232.        (message "Encoding base64..."))
  233.   (let ((work-buffer nil)
  234.     (counter 0)
  235.     (cols 0)
  236.     (bits 0)
  237.     (alphabet vm-mime-base64-alphabet)
  238.     inputpos)
  239.     (unwind-protect
  240.     (save-excursion
  241.       (setq work-buffer (generate-new-buffer " *vm-work*"))
  242.       (buffer-disable-undo work-buffer)
  243.       (if crlf
  244.           (progn
  245.         (or (markerp end) (setq end (vm-marker end)))
  246.         (vm-mime-lf-to-crlf-region start end)))
  247.       (if vm-mime-base64-encoder-program
  248.           (let ((status (apply 'vm-run-command-on-region
  249.                    start end work-buffer
  250.                    vm-mime-base64-encoder-program
  251.                    vm-mime-base64-encoder-switches)))
  252.         (if (not (eq status t))
  253.             (vm-mime-error "%s" (cdr status)))
  254.         (if B-encoding
  255.             (progn
  256.               ;; if we're B encoding, strip out the line breaks
  257.               (goto-char (point-min))
  258.               (while (search-forward "\n" nil t)
  259.             (delete-char -1)))))
  260.         (setq inputpos start)
  261.         (while (< inputpos end)
  262.           (setq bits (+ bits (char-after inputpos)))
  263.           (vm-increment counter)
  264.           (cond ((= counter 3)
  265.              (vm-insert-char (aref alphabet (lsh bits -18)) 1 nil
  266.                      work-buffer)
  267.              (vm-insert-char (aref alphabet (logand (lsh bits -12) 63))
  268.                      1 nil work-buffer)
  269.              (vm-insert-char (aref alphabet (logand (lsh bits -6) 63))
  270.                      1 nil work-buffer)
  271.              (vm-insert-char (aref alphabet (logand bits 63)) 1 nil
  272.                      work-buffer)
  273.              (setq cols (+ cols 4))
  274.              (cond ((= cols 72)
  275.                 (setq cols 0)
  276.                 (if (not B-encoding)
  277.                 (vm-insert-char ?\n 1 nil work-buffer))))
  278.              (setq bits 0 counter 0))
  279.             (t (setq bits (lsh bits 8))))
  280.           (vm-increment inputpos))
  281.         ;; write out any remaining bits with appropriate padding
  282.         (if (= counter 0)
  283.         nil
  284.           (setq bits (lsh bits (- 16 (* 8 counter))))
  285.           (vm-insert-char (aref alphabet (lsh bits -18)) 1 nil
  286.                   work-buffer)
  287.           (vm-insert-char (aref alphabet (logand (lsh bits -12) 63))
  288.                   1 nil work-buffer)
  289.           (if (= counter 1)
  290.           (vm-insert-char ?= 2 nil work-buffer)
  291.         (vm-insert-char (aref alphabet (logand (lsh bits -6) 63))
  292.                 1 nil work-buffer)
  293.         (vm-insert-char ?= 1 nil work-buffer)))
  294.         (if (> cols 0)
  295.         (vm-insert-char ?\n 1 nil work-buffer)))
  296.       (or (markerp end) (setq end (vm-marker end)))
  297.       (goto-char start)
  298.       (insert-buffer-substring work-buffer)
  299.       (delete-region (point) end)
  300.       (and (> (- end start) 200)
  301.            (message "Encoding base64... done"))
  302.       (- end start))
  303.       (and work-buffer (kill-buffer work-buffer)))))
  304.  
  305. (defun vm-mime-qp-decode-region (start end)
  306.   (and (> (- end start) 200)
  307.        (message "Decoding quoted-printable..."))
  308.   (let ((work-buffer nil)
  309.     (buf (current-buffer))
  310.     (case-fold-search nil)
  311.     (hex-digit-alist '((?0 .  0)  (?1 .  1)  (?2 .  2)  (?3 .  3)
  312.                (?4 .  4)  (?5 .  5)  (?6 .  6)  (?7 .  7)
  313.                (?8 .  8)  (?9 .  9)  (?A . 10)  (?B . 11)
  314.                (?C . 12)  (?D . 13)  (?E . 14)  (?F . 15)))
  315.     inputpos stop-point copy-point)
  316.     (unwind-protect
  317.     (save-excursion
  318.       (setq work-buffer (generate-new-buffer " *vm-work*"))
  319.       (buffer-disable-undo work-buffer)
  320.       (goto-char start)
  321.       (setq inputpos start)
  322.       (while (< inputpos end)
  323.         (skip-chars-forward "^=\n" end)
  324.         (setq stop-point (point))
  325.         (cond ((looking-at "\n")
  326.            ;; spaces or tabs before a hard line break must be ignored
  327.            (skip-chars-backward " \t")
  328.            (setq copy-point (point))
  329.            (goto-char stop-point))
  330.           (t (setq copy-point stop-point)))
  331.         (save-excursion
  332.           (set-buffer work-buffer)
  333.           (insert-buffer-substring buf inputpos copy-point))
  334.         (cond ((= (point) end) t)
  335.           ((looking-at "\n")
  336.            (vm-insert-char ?\n 1 nil work-buffer)
  337.            (forward-char))
  338.           (t ;; looking at =
  339.            (forward-char)
  340.            (cond ((looking-at "[0-9A-F][0-9A-F]")
  341.               (vm-insert-char (+ (* (cdr (assq (char-after (point))
  342.                                hex-digit-alist))
  343.                         16)
  344.                          (cdr (assq (char-after
  345.                              (1+ (point)))
  346.                             hex-digit-alist)))
  347.                       1 nil work-buffer)
  348.               (forward-char 2))
  349.              ((looking-at "\n") ; soft line break
  350.               (forward-char))
  351.              ((looking-at "\r")
  352.               ;; assume the user's goatloving
  353.               ;; delivery software didn't convert
  354.               ;; from Internet's CRLF newline
  355.               ;; convention to the local LF
  356.               ;; convention.
  357.               (forward-char))
  358.              ((looking-at "[ \t]")
  359.               ;; garbage added in transit
  360.               (skip-chars-forward " \t" end))
  361.              (t (vm-mime-error "something other than line break or hex digits after = in quoted-printable encoding")))))
  362.         (setq inputpos (point)))
  363.       (or (markerp end) (setq end (vm-marker end)))
  364.       (goto-char start)
  365.       (insert-buffer-substring work-buffer)
  366.       (delete-region (point) end))
  367.       (and work-buffer (kill-buffer work-buffer))))
  368.   (and (> (- end start) 200)
  369.        (message "Decoding quoted-printable... done")))
  370.  
  371. (defun vm-mime-qp-encode-region (start end &optional Q-encoding quote-from)
  372.   (and (> (- end start) 200)
  373.        (message "Encoding quoted-printable..."))
  374.   (let ((work-buffer nil)
  375.     (buf (current-buffer))
  376.     (cols 0)
  377.     (hex-digit-alist '((?0 .  0)  (?1 .  1)  (?2 .  2)  (?3 .  3)
  378.                (?4 .  4)  (?5 .  5)  (?6 .  6)  (?7 .  7)
  379.                (?8 .  8)  (?9 .  9)  (?A . 10)  (?B . 11)
  380.                (?C . 12)  (?D . 13)  (?E . 14)  (?F . 15)))
  381.     char inputpos)
  382.     (unwind-protect
  383.     (save-excursion
  384.       (setq work-buffer (generate-new-buffer " *vm-work*"))
  385.       (buffer-disable-undo work-buffer)
  386.       (setq inputpos start)
  387.       (while (< inputpos end)
  388.         (setq char (char-after inputpos))
  389.         (cond ((= char ?\n)
  390.            (vm-insert-char char 1 nil work-buffer)
  391.            (setq cols 0))
  392.           ((and (= char 32)
  393.             (not (= (1+ inputpos) end))
  394.             (not (= ?\n (char-after (1+ inputpos)))))
  395.            (vm-insert-char char 1 nil work-buffer)
  396.            (vm-increment cols))
  397.           ((or (< char 33) (> char 126) (= char 61)
  398.                (and quote-from (= cols 0) (let ((case-fold-search nil))
  399.                             (looking-at "From ")))
  400.                (and (= cols 0) (= char ?.)
  401.                 (looking-at "\\.\\(\n\\|\\'\\)")))
  402.            (vm-insert-char ?= 1 nil work-buffer)
  403.            (vm-insert-char (car (rassq (lsh char -4) hex-digit-alist))
  404.                    1 nil work-buffer)
  405.            (vm-insert-char (car (rassq (logand char 15)
  406.                            hex-digit-alist))
  407.                    1 nil work-buffer)
  408.            (setq cols (+ cols 3)))
  409.           (t (vm-insert-char char 1 nil work-buffer)
  410.              (vm-increment cols)))
  411.         (cond ((> cols 70)
  412.            (setq cols 0)
  413.            (if Q-encoding
  414.                nil
  415.              (vm-insert-char ?= 1 nil work-buffer)
  416.              (vm-insert-char ?\n 1 nil work-buffer))))
  417.         (vm-increment inputpos))
  418.       (or (markerp end) (setq end (vm-marker end)))
  419.       (goto-char start)
  420.       (insert-buffer-substring work-buffer)
  421.       (delete-region (point) end)
  422.       (and (> (- end start) 200)
  423.            (message "Encoding quoted-printable... done"))
  424.       (- end start))
  425.       (and work-buffer (kill-buffer work-buffer)))))
  426.  
  427. (defun vm-decode-mime-message-headers (m)
  428.   (let ((case-fold-search t)
  429.     (buffer-read-only nil)
  430.     charset encoding match-start match-end start end)
  431.     (save-excursion
  432.       (goto-char (vm-headers-of m))
  433.       (while (re-search-forward vm-mime-encoded-word-regexp (vm-text-of m) t)
  434.     (setq match-start (match-beginning 0)
  435.           match-end (match-end 0)
  436.           charset (buffer-substring (match-beginning 1) (match-end 1))
  437.           encoding (buffer-substring (match-beginning 2) (match-end 2))
  438.           start (match-beginning 3)
  439.           end (vm-marker (match-end 3)))
  440.     ;; don't change anything if we can't display the
  441.     ;; character set properly.
  442.     (if (not (vm-mime-charset-internally-displayable-p charset))
  443.         nil
  444.       (delete-region end match-end)
  445.       (condition-case data
  446.           (cond ((string-match "B" encoding)
  447.              (vm-mime-B-decode-region start end))
  448.             ((string-match "Q" encoding)
  449.              (vm-mime-Q-decode-region start end))
  450.             (t (vm-mime-error "unknown encoded word encoding, %s"
  451.                       encoding)))
  452.         (vm-mime-error (apply 'message (cdr data))
  453.                (goto-char start)
  454.                (insert "**invalid encoded word**")
  455.                (delete-region (point) end)))
  456.       (vm-mime-charset-decode-region charset start end)
  457.       (delete-region match-start start))))))
  458.  
  459. (defun vm-decode-mime-encoded-words ()
  460.   (let ((case-fold-search t)
  461.     (buffer-read-only nil)
  462.     charset encoding match-start match-end start end)
  463.     (save-excursion
  464.       (goto-char (point-min))
  465.       (while (re-search-forward vm-mime-encoded-word-regexp nil t)
  466.     (setq match-start (match-beginning 0)
  467.           match-end (match-end 0)
  468.           charset (buffer-substring (match-beginning 1) (match-end 1))
  469.           encoding (buffer-substring (match-beginning 2) (match-end 2))
  470.           start (match-beginning 3)
  471.           end (vm-marker (match-end 3)))
  472.     ;; don't change anything if we can't display the
  473.     ;; character set properly.
  474.     (if (not (vm-mime-charset-internally-displayable-p charset))
  475.         nil
  476.       (delete-region end match-end)
  477.       (condition-case data
  478.           (cond ((string-match "B" encoding)
  479.              (vm-mime-B-decode-region start end))
  480.             ((string-match "Q" encoding)
  481.              (vm-mime-Q-decode-region start end))
  482.             (t (vm-mime-error "unknown encoded word encoding, %s"
  483.                       encoding)))
  484.         (vm-mime-error (apply 'message (cdr data))
  485.                (goto-char start)
  486.                (insert "**invalid encoded word**")
  487.                (delete-region (point) end)))
  488.       (vm-mime-charset-decode-region charset start end)
  489.       (delete-region match-start start))))))
  490.  
  491. (defun vm-decode-mime-encoded-words-in-string (string)
  492.   (if (and vm-display-using-mime
  493.        (string-match vm-mime-encoded-word-regexp string))
  494.       (vm-with-string-as-temp-buffer string 'vm-decode-mime-encoded-words)
  495.     string ))
  496.  
  497. (defun vm-reencode-mime-encoded-words ()
  498.   (let ((charset nil)
  499.     start coding pos q-encoding
  500.     old-size
  501.     (case-fold-search t)
  502.     (done nil))
  503.     (save-excursion
  504.       (setq start (point-min))
  505.       (while (not done)
  506.     (setq charset (get-text-property start 'vm-charset))
  507.     (setq pos (next-single-property-change start 'vm-charset))
  508.     (or pos (setq pos (point-max) done t))
  509.     (if charset
  510.         (progn
  511.           (if (setq coding (get-text-property start 'vm-coding))
  512.           (progn
  513.             (setq old-size (buffer-size))
  514.             (encode-coding-region start pos coding)
  515.             (setq pos (+ pos (- (buffer-size) old-size)))))
  516.           (setq pos
  517.             (+ start 
  518.                (if (setq q-encoding
  519.                  (string-match "^iso-8859-\\|^us-ascii"
  520.                            charset))
  521.                (vm-mime-Q-encode-region start pos)
  522.              (vm-mime-B-encode-region start pos))))
  523.           (goto-char pos)
  524.           (insert "?=")
  525.           (setq pos (point))
  526.           (goto-char start)
  527.           (insert "=?" charset "?" (if q-encoding "Q" "B") "?")))
  528.     (setq start pos)))))
  529.  
  530. (defun vm-reencode-mime-encoded-words-in-string (string)
  531.   (if (and vm-display-using-mime
  532.        (text-property-any 0 (length string) 'vm-string t string))
  533.       (vm-with-string-as-temp-buffer string 'vm-reencode-mime-encoded-words)
  534.     string ))
  535.  
  536. (fset 'vm-mime-parse-content-header 'vm-parse-structured-header)
  537.  
  538. (defun vm-mime-get-header-contents (header-name-regexp)
  539.   (let ((contents nil)
  540.     regexp)
  541.     (setq regexp (concat "^\\(" header-name-regexp "\\)\\|\\(^$\\)"))
  542.     (save-excursion
  543.       (let ((case-fold-search t))
  544.     (if (and (re-search-forward regexp nil t)
  545.          (match-beginning 1)
  546.          (progn (goto-char (match-beginning 0))
  547.             (vm-match-header)))
  548.         (vm-matched-header-contents)
  549.       nil )))))
  550.  
  551. (defun vm-mime-parse-entity (&optional m default-type default-encoding)
  552.   (let ((case-fold-search t) version type qtype encoding id description
  553.     disposition qdisposition boundary boundary-regexp start
  554.     multipart-list c-t c-t-e done p returnval)
  555.     (catch 'return-value
  556.       (save-excursion
  557.     (if m
  558.         (progn
  559.           (setq m (vm-real-message-of m))
  560.           (set-buffer (vm-buffer-of m))))
  561.     (save-excursion
  562.       (save-restriction
  563.         (if m
  564.         (progn
  565.           (setq version (vm-get-header-contents m "MIME-Version:")
  566.             version (car (vm-mime-parse-content-header version))
  567.             type (vm-get-header-contents m "Content-Type:")
  568.             qtype (vm-mime-parse-content-header type ?\; t)
  569.             type (vm-mime-parse-content-header type ?\;)
  570.             encoding (or (vm-get-header-contents
  571.                       m "Content-Transfer-Encoding:")
  572.                      "7bit")
  573.             encoding (or (car
  574.                       (vm-mime-parse-content-header encoding))
  575.                      "7bit")
  576.             id (vm-get-header-contents m "Content-ID:")
  577.             id (car (vm-mime-parse-content-header id))
  578.             description (vm-get-header-contents
  579.                      m "Content-Description:")
  580.             description (and description
  581.                      (if (string-match "^[ \t\n]$"
  582.                                description)
  583.                          nil
  584.                        description))
  585.             disposition (vm-get-header-contents
  586.                      m "Content-Disposition:")
  587.             qdisposition (and disposition
  588.                       (vm-mime-parse-content-header
  589.                        disposition ?\; t))
  590.             disposition (and disposition
  591.                      (vm-mime-parse-content-header
  592.                       disposition ?\;)))
  593.           (widen)
  594.           (narrow-to-region (vm-headers-of m) (vm-text-end-of m)))
  595.           (goto-char (point-min))
  596.           (setq type (vm-mime-get-header-contents "Content-Type:")
  597.             qtype (or (vm-mime-parse-content-header type ?\; t)
  598.                   default-type)
  599.             type (or (vm-mime-parse-content-header type ?\;)
  600.                  default-type)
  601.             encoding (or (vm-mime-get-header-contents
  602.                   "Content-Transfer-Encoding:")
  603.                  default-encoding)
  604.             encoding (or (car (vm-mime-parse-content-header encoding))
  605.                  default-encoding)
  606.             id (vm-mime-get-header-contents "Content-ID:")
  607.             id (car (vm-mime-parse-content-header id))
  608.             description (vm-mime-get-header-contents
  609.                  "Content-Description:")
  610.             description (and description (if (string-match "^[ \t\n]+$"
  611.                                    description)
  612.                              nil
  613.                            description))
  614.             disposition (vm-mime-get-header-contents
  615.                  "Content-Disposition:")
  616.             qdisposition (and disposition
  617.                       (vm-mime-parse-content-header
  618.                        disposition ?\; t))
  619.             disposition (and disposition
  620.                      (vm-mime-parse-content-header
  621.                       disposition ?\;))))
  622.         (cond ((null m) t)
  623.           ((null version)
  624.            (throw 'return-value 'none))
  625.           ((or vm-mime-ignore-mime-version (string= version "1.0")) t)
  626.           (t (vm-mime-error "Unsupported MIME version: %s" version)))
  627.         (cond ((and m (null type))
  628.            (throw 'return-value
  629.               (vector '("text/plain" "charset=us-ascii")
  630.                   '("text/plain" "charset=us-ascii")
  631.                   encoding id description
  632.                   disposition qdisposition
  633.                   (vm-headers-of m)
  634.                   (vm-text-of m)
  635.                   (vm-text-end-of m)
  636.                   nil nil nil )))
  637.           ((null type)
  638.            (goto-char (point-min))
  639.            (or (re-search-forward "^\n\\|\n\\'" nil t)
  640.                (vm-mime-error "MIME part missing header/body separator line"))
  641.            (vector default-type default-type
  642.                encoding id description
  643.                disposition qdisposition
  644.                (vm-marker (point-min))
  645.                (vm-marker (point))
  646.                (vm-marker (point-max))
  647.                nil nil nil ))
  648.           ((null (string-match "[^/ ]+/[^/ ]+" (car type)))
  649.            (vm-mime-error "Malformed MIME content type: %s" (car type)))
  650.           ((and (string-match "^multipart/\\|^message/" (car type))
  651.             (null (string-match "^\\(7bit\\|8bit\\|binary\\)$"
  652.                         encoding)))
  653.            (vm-mime-error "Opaque transfer encoding used with multipart or message type: %s, %s" (car type) encoding))
  654.           ((and (string-match "^message/partial$" (car type))
  655.             (null (string-match "^7bit$" encoding)))
  656.            (vm-mime-error "Non-7BIT transfer encoding used with message/partial message: %s" encoding))
  657.           ((string-match "^multipart/digest" (car type))
  658.            (setq c-t '("message/rfc822")
  659.              c-t-e "7bit"))
  660.           ((string-match "^multipart/" (car type))
  661.            (setq c-t '("text/plain" "charset=us-ascii")
  662.              c-t-e "7bit")) ; below
  663.           ((string-match "^message/\\(rfc822\\|news\\)" (car type))
  664.            (setq c-t '("text/plain" "charset=us-ascii")
  665.              c-t-e "7bit")
  666.            (goto-char (point-min))
  667.            (or (re-search-forward "^\n\\|\n\\'" nil t)
  668.                (vm-mime-error "MIME part missing header/body separator line"))
  669.            (throw 'return-value
  670.               (vector type qtype encoding id description
  671.                   disposition qdisposition
  672.                   (vm-marker (point-min))
  673.                   (vm-marker (point))
  674.                   (vm-marker (point-max))
  675.                   (list
  676.                    (save-restriction
  677.                      (narrow-to-region (point) (point-max))
  678.                      (vm-mime-parse-entity-safe nil c-t
  679.                                 c-t-e)))
  680.                   nil nil )))
  681.           (t
  682.            (goto-char (point-min))
  683.            (or (re-search-forward "^\n\\|\n\\'" nil t)
  684.                (vm-mime-error "MIME part missing header/body separator line"))
  685.            (throw 'return-value
  686.               (vector type qtype encoding id description
  687.                   disposition qdisposition
  688.                   (vm-marker (point-min))
  689.                   (vm-marker (point))
  690.                   (vm-marker (point-max))
  691.                   nil nil nil ))))
  692.         (setq p (cdr type)
  693.           boundary nil)
  694.         (while p
  695.           (if (string-match "^boundary=" (car p))
  696.           (setq boundary (car (vm-parse (car p) "=\\(.+\\)"))
  697.             p nil)
  698.         (setq p (cdr p))))
  699.         (or boundary
  700.         (vm-mime-error
  701.          "Boundary parameter missing in %s type specification"
  702.          (car type)))
  703.         ;; the \' in the regexp is to "be liberal" in the
  704.         ;; face of broken software that does not add a line
  705.         ;; break after the final boundary of a nested
  706.         ;; multipart entity.
  707.         (setq boundary-regexp
  708.           (concat "^--" (regexp-quote boundary)
  709.               "\\(--\\)?[ \t]*\\(\n\\|\\'\\)"))
  710.         (goto-char (point-min))
  711.         (setq start nil
  712.           multipart-list nil
  713.           done nil)
  714.         (while (and (not done) (re-search-forward boundary-regexp nil t))
  715.           (cond ((null start)
  716.              (setq start (match-end 0)))
  717.             (t
  718.              (and (match-beginning 1)
  719.               (setq done t))
  720.              (save-excursion
  721.                (save-restriction
  722.              (narrow-to-region start (1- (match-beginning 0)))
  723.              (setq start (match-end 0))
  724.              (setq multipart-list
  725.                    (cons (vm-mime-parse-entity-safe nil c-t c-t-e)
  726.                      multipart-list)))))))
  727.         (if (not done)
  728.         (vm-mime-error "final %s boundary missing" boundary))
  729.         (goto-char (point-min))
  730.         (or (re-search-forward "^\n\\|\n\\'" nil t)
  731.         (vm-mime-error "MIME part missing header/body separator line"))
  732.         (vector type qtype encoding id description
  733.             disposition qdisposition
  734.             (vm-marker (point-min))
  735.             (vm-marker (point))
  736.             (vm-marker (point-max))
  737.             (nreverse multipart-list)
  738.             nil nil )))))))
  739.  
  740. (defun vm-mime-parse-entity-safe (&optional m c-t c-t-e)
  741.   (or c-t (setq c-t '("text/plain" "charset=us-ascii")))
  742.   ;; don't let subpart parse errors make the whole parse fail.  use default
  743.   ;; type if the parse fails.
  744.   (condition-case error-data
  745.       (vm-mime-parse-entity nil c-t c-t-e)
  746.     (vm-mime-error
  747.      (let ((header (if m
  748.                (vm-headers-of m)
  749.              (vm-marker (point-min))))
  750.        (text (if m
  751.              (vm-text-of m)
  752.            (save-excursion
  753.              (re-search-forward "^\n\\|\n\\'"
  754.                     nil 0)
  755.              (vm-marker (point)))))
  756.        (text-end (if m
  757.              (vm-text-end-of m)
  758.                (vm-marker (point-max)))))
  759.      (vector c-t c-t
  760.          (vm-determine-proper-content-transfer-encoding text text-end)
  761.          nil
  762.          ;; cram the error message into the description slot
  763.          (car (cdr error-data))
  764.          ;; mark as an attachment to improve the chance that the user
  765.          ;; will see the description.
  766.          '("attachment") '("attachment")
  767.          header
  768.          text
  769.          text-end
  770.          nil nil nil)))))
  771.  
  772. (defun vm-mime-get-xxx-parameter (layout name param-list)
  773.   (let ((match-end (1+ (length name)))
  774.     (name-regexp (concat (regexp-quote name) "="))
  775.     (case-fold-search t)
  776.     (done nil))
  777.     (while (and param-list (not done))
  778.       (if (and (string-match name-regexp (car param-list))
  779.            (= (match-end 0) match-end))
  780.       (setq done t)
  781.     (setq param-list (cdr param-list))))
  782.     (and (car param-list) (car (vm-parse (car param-list) "=\\(.*\\)")))))
  783.  
  784. (defun vm-mime-get-parameter (layout name)
  785.   (vm-mime-get-xxx-parameter layout name (cdr (vm-mm-layout-type layout))))
  786.  
  787. (defun vm-mime-get-disposition-parameter (layout name)
  788.   (vm-mime-get-xxx-parameter layout name
  789.                  (cdr (vm-mm-layout-disposition layout))))
  790.  
  791. (defun vm-mime-insert-mime-body (layout)
  792.   (vm-insert-region-from-buffer (marker-buffer (vm-mm-layout-body-start layout))
  793.                 (vm-mm-layout-body-start layout)
  794.                 (vm-mm-layout-body-end layout)))
  795.  
  796. (defun vm-mime-insert-mime-headers (layout)
  797.   (vm-insert-region-from-buffer (marker-buffer (vm-mm-layout-body-start layout))
  798.                 (vm-mm-layout-header-start layout)
  799.                 (vm-mm-layout-body-start layout))
  800.   (if (and (not (bobp)) (char-equal (char-after (1- (point))) ?\n))
  801.       (delete-char -1)))
  802.  
  803. (defun vm-make-presentation-copy (m)
  804.   (let ((mail-buffer (current-buffer))
  805.     b mm
  806.     (real-m (vm-real-message-of m))
  807.     (modified (buffer-modified-p))
  808.     (coding-system-for-read 'binary)
  809.     (coding-system-for-write 'binary))
  810.     (cond ((or (null vm-presentation-buffer-handle)
  811.            (null (buffer-name vm-presentation-buffer-handle)))
  812.        (setq b (generate-new-buffer (concat (buffer-name)
  813.                         " Presentation")))
  814.        (save-excursion
  815.          (set-buffer b)
  816.          (if (fboundp 'buffer-disable-undo)
  817.          (buffer-disable-undo (current-buffer))
  818.            ;; obfuscation to make the v19 compiler not whine
  819.            ;; about obsolete functions.
  820.            (let ((x 'buffer-flush-undo))
  821.          (funcall x (current-buffer))))
  822.          (setq mode-name "VM Presentation"
  823.            major-mode 'vm-presentation-mode
  824.            vm-message-pointer (list nil)
  825.            vm-mail-buffer mail-buffer
  826.            mode-popup-menu (and vm-use-menus vm-popup-menu-on-mouse-3
  827.                     (vm-menu-support-possible-p)
  828.                     (vm-menu-mode-menu))
  829.            ;; Default to binary file type for DOS/NT.
  830.            buffer-file-type t
  831.            ;; Tell XEmacs/MULE not to mess with the text on writes.
  832.            buffer-read-only t
  833.            mode-line-format vm-mode-line-format)
  834.          ;; scroll in place messes with scroll-up and this loses
  835.          (defvar scroll-in-place)
  836.          (make-local-variable 'scroll-in-place)
  837.          (setq scroll-in-place nil)
  838.          (and vm-xemacs-mule-p
  839.           (set-buffer-file-coding-system 'binary t))
  840.          (cond (vm-fsfemacs-p
  841.             ;; need to do this outside the let because
  842.             ;; loading disp-table initializes
  843.             ;; standard-display-table.
  844.             (require 'disp-table)
  845.             (let* ((standard-display-table
  846.                 (copy-sequence standard-display-table)))
  847.               (standard-display-european t)
  848.               (setq buffer-display-table standard-display-table))))
  849.          (if (and vm-mutable-frames vm-frame-per-folder
  850.               (vm-multiple-frames-possible-p))
  851.          (vm-set-hooks-for-frame-deletion))
  852.          (use-local-map vm-mode-map)
  853.          (and (vm-toolbar-support-possible-p) vm-use-toolbar
  854.           (vm-toolbar-install-toolbar))
  855.          (and (vm-menu-support-possible-p)
  856.           (vm-menu-install-menus))
  857.          (run-hooks 'vm-presentation-mode-hook))
  858.        (setq vm-presentation-buffer-handle b)))
  859.     (setq b vm-presentation-buffer-handle
  860.       vm-presentation-buffer vm-presentation-buffer-handle
  861.       vm-mime-decoded nil)
  862.     (save-excursion
  863.       (set-buffer (vm-buffer-of real-m))
  864.       (save-restriction
  865.     (widen)
  866.     ;; must reference this now so that headers will be in
  867.     ;; their final position before the message is copied.
  868.     ;; otherwise the vheader offset computed below will be
  869.     ;; wrong.
  870.     (vm-vheaders-of real-m)
  871.     (set-buffer b)
  872.     (widen)
  873.     (let ((buffer-read-only nil)
  874.           ;; disable read-only text properties
  875.           (inhibit-read-only t)
  876.           (modified (buffer-modified-p)))
  877.       (unwind-protect
  878.           (progn
  879.         (erase-buffer)
  880.         (insert-buffer-substring (vm-buffer-of real-m)
  881.                      (vm-start-of real-m)
  882.                      (vm-end-of real-m)))
  883.         (set-buffer-modified-p modified)))
  884.     (setq mm (copy-sequence m))
  885.     (vm-set-location-data-of mm (vm-copy (vm-location-data-of m)))
  886.     (set-marker (vm-start-of mm) (point-min))
  887.     (set-marker (vm-headers-of mm) (+ (vm-start-of mm)
  888.                       (- (vm-headers-of real-m)
  889.                          (vm-start-of real-m))))
  890.     (set-marker (vm-vheaders-of mm) (+ (vm-start-of mm)
  891.                        (- (vm-vheaders-of real-m)
  892.                           (vm-start-of real-m))))
  893.     (set-marker (vm-text-of mm) (+ (vm-start-of mm)
  894.                        (- (vm-text-of real-m)
  895.                       (vm-start-of real-m))))
  896.     (set-marker (vm-text-end-of mm) (+ (vm-start-of mm)
  897.                        (- (vm-text-end-of real-m)
  898.                           (vm-start-of real-m))))
  899.     (set-marker (vm-end-of mm) (+ (vm-start-of mm)
  900.                       (- (vm-end-of real-m)
  901.                      (vm-start-of real-m))))
  902.     (setcar vm-message-pointer mm)))))
  903.  
  904. (fset 'vm-presentation-mode 'vm-mode)
  905. (put 'vm-presentation-mode 'mode-class 'special)
  906.  
  907. (defvar buffer-file-coding-system)
  908.  
  909. (defun vm-determine-proper-charset (beg end)
  910.   (save-excursion
  911.     (save-restriction
  912.       (narrow-to-region beg end)
  913.       (catch 'done
  914.     (goto-char (point-min))
  915.     (if vm-xemacs-mule-p
  916.         (let ((charsets (delq 'ascii (charsets-in-region beg end))))
  917.           (cond ((null charsets)
  918.              "us-ascii")
  919.             ((cdr charsets)
  920.              (or (car (cdr
  921.                    (assq (coding-system-name
  922.                       buffer-file-coding-system)
  923.                      vm-mime-mule-coding-to-charset-alist)))
  924.              "iso-2022-jp"))
  925.             (t
  926.              (or (car (cdr
  927.                    (assoc
  928.                 (car charsets)
  929.                 vm-mime-mule-charset-to-charset-alist)))
  930.              "unknown"))))
  931.       (and (re-search-forward "[^\000-\177]" nil t)
  932.            (throw 'done (or vm-mime-8bit-composition-charset
  933.                 "iso-8859-1")))
  934.       (throw 'done vm-mime-7bit-composition-charset))))))
  935.  
  936. (defun vm-determine-proper-content-transfer-encoding (beg end)
  937.   (save-excursion
  938.     (save-restriction
  939.       (narrow-to-region beg end)
  940.       (catch 'done
  941.     (goto-char (point-min))
  942.     (and (re-search-forward "[\000\015]" nil t)
  943.          (throw 'done "binary"))
  944.  
  945.     (let ((toolong nil) bol)
  946.       (goto-char (point-min))
  947.       (setq bol (point))
  948.       (while (and (not (eobp)) (not toolong))
  949.         (forward-line)
  950.         (setq toolong (> (- (point) bol) 998)
  951.           bol (point)))
  952.       (and toolong (throw 'done "binary")))
  953.      
  954.     (goto-char (point-min))
  955.     (and (re-search-forward "[\200-\377]" nil t)
  956.          (throw 'done "8bit"))
  957.  
  958.     "7bit"))))
  959.  
  960. (defun vm-mime-types-match (type type/subtype)
  961.   (let ((case-fold-search t))
  962.     (cond ((string-match "/" type)
  963.        (if (and (string-match (regexp-quote type) type/subtype)
  964.             (equal 0 (match-beginning 0))
  965.             (equal (length type/subtype) (match-end 0)))
  966.            t
  967.          nil ))
  968.       ((and (string-match (regexp-quote type) type/subtype)
  969.         (equal 0 (match-beginning 0))
  970.         (equal (save-match-data
  971.              (string-match "/" type/subtype (match-end 0)))
  972.                (match-end 0)))))))
  973.  
  974. (defvar native-sound-only-on-console)
  975.  
  976. (defun vm-mime-can-display-internal (layout)
  977.   (let ((type (car (vm-mm-layout-type layout))))
  978.     (cond ((vm-mime-types-match "image/jpeg" type)
  979.        (and vm-xemacs-p
  980.         (featurep 'jpeg)
  981.         (eq (device-type) 'x)))
  982.       ((vm-mime-types-match "image/gif" type)
  983.        (and vm-xemacs-p
  984.         (featurep 'gif)
  985.         (eq (device-type) 'x)))
  986.       ((vm-mime-types-match "image/png" type)
  987.        (and vm-xemacs-p
  988.         (featurep 'png)
  989.         (eq (device-type) 'x)))
  990.       ((vm-mime-types-match "image/tiff" type)
  991.        (and vm-xemacs-p
  992.         (featurep 'tiff)
  993.         (eq (device-type) 'x)))
  994.       ((vm-mime-types-match "audio/basic" type)
  995.        (and vm-xemacs-p
  996.         (or (featurep 'native-sound)
  997.             (featurep 'nas-sound))
  998.         (or (device-sound-enabled-p)
  999.             (and (featurep 'native-sound)
  1000.              (not native-sound-only-on-console)
  1001.              (eq (device-type) 'x)))))
  1002.       ((vm-mime-types-match "multipart" type) t)
  1003.       ((vm-mime-types-match "message/external-body" type) nil)
  1004.       ((vm-mime-types-match "message" type) t)
  1005.       ((or (vm-mime-types-match "text/plain" type)
  1006.            (and (vm-mime-types-match "text/enriched" type)
  1007.             (fboundp 'enriched-mode)))
  1008.        (let ((charset (or (vm-mime-get-parameter layout "charset")
  1009.                   "us-ascii")))
  1010.          (vm-mime-charset-internally-displayable-p charset)))
  1011. ;; the problems with this are making me gryte at the sky.
  1012. ;; let the tripe be pumped to lynx or netscape.
  1013. ;;      ((vm-mime-types-match "text/html" type)
  1014. ;;       (condition-case ()
  1015. ;;           (progn (require 'w3)
  1016. ;;              (fboundp 'w3-region))
  1017. ;;         (error nil)))
  1018.       (t nil))))
  1019.  
  1020. (defun vm-mime-can-convert (type)
  1021.   (let ((alist vm-mime-type-converter-alist)
  1022.     ;; fake layout. make it the wrong length so an error will
  1023.     ;; be signaled if vm-mime-can-display-internal ever asks
  1024.     ;; for one of the other fields
  1025.     (fake-layout (make-vector 1 (list nil)))
  1026.     (done nil))
  1027.     (while (and alist (not done))
  1028.       (cond ((and (vm-mime-types-match (car (car alist)) type)
  1029.           (or (progn
  1030.             (setcar (aref fake-layout 0) (nth 1 (car alist)))
  1031.             (vm-mime-can-display-internal fake-layout))
  1032.               (vm-mime-find-external-viewer (nth 1 (car alist)))))
  1033.          (setq done t))
  1034.         (t (setq alist (cdr alist)))))
  1035.     (and alist (car alist))))
  1036.  
  1037. (defun vm-mime-convert-undisplayable-layout (layout)
  1038.   (let ((ooo (vm-mime-can-convert (car (vm-mm-layout-type layout)))))
  1039.     (message "Converting %s to %s..."
  1040.             (car (vm-mm-layout-type layout))
  1041.             (nth 1 ooo))
  1042.     (save-excursion
  1043.       (set-buffer (generate-new-buffer " *mime object*"))
  1044.       (setq vm-message-garbage-alist
  1045.         (cons (cons (current-buffer) 'kill-buffer)
  1046.           vm-message-garbage-alist))
  1047.       (vm-mime-insert-mime-body layout)
  1048.       (vm-mime-transfer-decode-region layout (point-min) (point-max))
  1049.       (call-process-region (point-min) (point-max) shell-file-name
  1050.                t t nil shell-command-switch (nth 2 ooo))
  1051.       (goto-char (point-min))
  1052.       (insert "Content-Type: " (nth 1 ooo) "\n")
  1053.       (insert "Content-Transfer-Encoding: binary\n\n")
  1054.       (set-buffer-modified-p nil)
  1055.       (message "Converting %s to %s... done"
  1056.             (car (vm-mm-layout-type layout))
  1057.             (nth 1 ooo))
  1058.       (vector (list (nth 1 ooo))
  1059.           (list (nth 1 ooo))
  1060.           "binary"
  1061.           (vm-mm-layout-id layout)
  1062.           (vm-mm-layout-description layout)
  1063.           (vm-mm-layout-disposition layout)
  1064.           (vm-mm-layout-qdisposition layout)
  1065.           (vm-marker (point-min))
  1066.           (vm-marker (point))
  1067.           (vm-marker (point-max))
  1068.           nil
  1069.           nil
  1070.           nil))))
  1071.  
  1072. (defun vm-mime-should-display-button (layout dont-honor-content-disposition)
  1073.   (if (and vm-honor-mime-content-disposition
  1074.        (not dont-honor-content-disposition)
  1075.        (vm-mm-layout-disposition layout))
  1076.       (let ((case-fold-search t))
  1077.     (string-match "^attachment$" (car (vm-mm-layout-disposition layout))))
  1078.     (let ((i-list vm-auto-displayed-mime-content-types)
  1079.       (type (car (vm-mm-layout-type layout)))
  1080.       (matched nil))
  1081.       (if (eq i-list t)
  1082.       nil
  1083.     (while (and i-list (not matched))
  1084.       (if (vm-mime-types-match (car i-list) type)
  1085.           (setq matched t)
  1086.         (setq i-list (cdr i-list))))
  1087.     (not matched) ))))
  1088.  
  1089. (defun vm-mime-should-display-internal (layout dont-honor-content-disposition)
  1090.   (if (and vm-honor-mime-content-disposition
  1091.        (not dont-honor-content-disposition)
  1092.        (vm-mm-layout-disposition layout))
  1093.       (let ((case-fold-search t))
  1094.     (string-match "^inline$" (car (vm-mm-layout-disposition layout))))
  1095.     (let ((i-list vm-mime-internal-content-types)
  1096.       (type (car (vm-mm-layout-type layout)))
  1097.       (matched nil))
  1098.       (if (eq i-list t)
  1099.       t
  1100.     (while (and i-list (not matched))
  1101.       (if (vm-mime-types-match (car i-list) type)
  1102.           (setq matched t)
  1103.         (setq i-list (cdr i-list))))
  1104.     matched ))))
  1105.  
  1106. (defun vm-mime-find-external-viewer (type)
  1107.   (let ((e-alist vm-mime-external-content-types-alist)
  1108.     (matched nil))
  1109.     (while (and e-alist (not matched))
  1110.       (if (and (vm-mime-types-match (car (car e-alist)) type)
  1111.            (cdr (car e-alist)))
  1112.       (setq matched (cdr (car e-alist)))
  1113.     (setq e-alist (cdr e-alist))))
  1114.     matched ))
  1115. (fset 'vm-mime-should-display-external 'vm-mime-find-external-viewer)
  1116.  
  1117. (defun vm-mime-delete-button-maybe (extent)
  1118.   (let ((buffer-read-only))
  1119.     ;; if displayed MIME object should replace the button
  1120.     ;; remove the button now.
  1121.     (cond ((vm-extent-property extent 'vm-mime-disposable)
  1122.        (delete-region (vm-extent-start-position extent)
  1123.               (vm-extent-end-position extent))
  1124.        (vm-detach-extent extent)))))
  1125.  
  1126. (defun vm-decode-mime-message ()
  1127.   "Decode the MIME objects in the current message.
  1128.  
  1129. The first time this command is run on a message, decoding is done.
  1130. The second time, buttons for all the objects are displayed instead.
  1131. The third time, the raw, undecoded data is displayed.
  1132.  
  1133. If decoding, the decoded objects might be displayed immediately, or
  1134. buttons might be displayed that you need to activate to view the
  1135. object.  See the documentation for the variables
  1136.  
  1137.     vm-auto-displayed-mime-content-types
  1138.     vm-mime-internal-content-types
  1139.     vm-mime-external-content-types-alist
  1140.  
  1141. to see how to control whether you see buttons or objects.
  1142.  
  1143. If the variable vm-mime-display-function is set, then its value
  1144. is called as a function with no arguments, and none of the
  1145. actions mentioned in the preceding paragraphs are done.  At the
  1146. time of the call, the current buffer will be the presentation
  1147. buffer for the folder and a copy of the current message will be
  1148. in the buffer.  The function is expected to make the message
  1149. `MIME presentable' to the user in whatever manner it sees fit."
  1150.   (interactive)
  1151.   (vm-follow-summary-cursor)
  1152.   (vm-select-folder-buffer)
  1153.   (vm-check-for-killed-summary)
  1154.   (vm-check-for-killed-presentation)
  1155.   (vm-error-if-folder-empty)
  1156.   (if (and (not vm-display-using-mime)
  1157.        (null vm-mime-display-function))
  1158.       (error "MIME display disabled, set vm-display-using-mime non-nil to enable."))
  1159.   (if vm-mime-display-function
  1160.       (progn
  1161.     (vm-make-presentation-copy (car vm-message-pointer))
  1162.     (set-buffer vm-presentation-buffer)
  1163.     (funcall vm-mime-display-function))
  1164.     (if vm-mime-decoded
  1165.     (if (eq vm-mime-decoded 'decoded)
  1166.         (let ((vm-preview-read-messages nil)
  1167.           (vm-auto-decode-mime-messages t)
  1168.           (vm-honor-mime-content-disposition nil)
  1169.           (vm-auto-displayed-mime-content-types '("multipart")))
  1170.           (setq vm-mime-decoded nil)
  1171.           (intern (buffer-name) vm-buffers-needing-display-update)
  1172.           (save-excursion
  1173.         (vm-preview-current-message))
  1174.           (setq vm-mime-decoded 'buttons))
  1175.       (let ((vm-preview-read-messages nil)
  1176.         (vm-auto-decode-mime-messages nil))
  1177.         (intern (buffer-name) vm-buffers-needing-display-update)
  1178.         (vm-preview-current-message)))
  1179.       (let ((layout (vm-mm-layout (car vm-message-pointer)))
  1180.         (m (car vm-message-pointer)))
  1181.     (message "Decoding MIME message...")
  1182.     (cond ((stringp layout)
  1183.            (error "Invalid MIME message: %s" layout)))
  1184.     (if (vm-mime-plain-message-p m)
  1185.         (error "Message needs no decoding."))
  1186.     (or vm-presentation-buffer
  1187.         ;; maybe user killed it
  1188.         (error "No presentation buffer."))
  1189.     (set-buffer vm-presentation-buffer)
  1190.     (if (and (interactive-p) (eq vm-system-state 'previewing))
  1191.         (let ((vm-display-using-mime nil))
  1192.           (vm-show-current-message)))
  1193.     (setq m (car vm-message-pointer))
  1194.     (vm-save-restriction
  1195.      (widen)
  1196.      (goto-char (vm-text-of m))
  1197.      (let ((buffer-read-only nil)
  1198.            (modified (buffer-modified-p)))
  1199.        (unwind-protect
  1200.            (save-excursion
  1201.          (and (not (eq (vm-mm-encoded-header m) 'none))
  1202.               (vm-decode-mime-message-headers m))
  1203.          (if (vectorp layout)
  1204.              (progn
  1205.                (vm-decode-mime-layout layout)
  1206.                (delete-region (point) (point-max))))
  1207.          (vm-energize-urls)
  1208.          (vm-highlight-headers-maybe)
  1209.          (vm-energize-headers-and-xfaces))
  1210.          (set-buffer-modified-p modified))))
  1211.     (save-excursion (set-buffer vm-mail-buffer)
  1212.             (setq vm-mime-decoded 'decoded))
  1213.     (intern (buffer-name vm-mail-buffer) vm-buffers-needing-display-update)
  1214.     (vm-update-summary-and-mode-line)
  1215.     (message "Decoding MIME message... done"))))
  1216.   (vm-display nil nil '(vm-decode-mime-message)
  1217.           '(vm-decode-mime-message reading-message)))
  1218.  
  1219. (defun vm-decode-mime-layout (layout &optional dont-honor-c-d)
  1220.   (let ((modified (buffer-modified-p)) type type-no-subtype (extent nil))
  1221.     (unwind-protect
  1222.     (progn
  1223.       (if (not (vectorp layout))
  1224.           (progn
  1225.         (setq extent layout
  1226.               layout (vm-extent-property extent 'vm-mime-layout))
  1227.         (goto-char (vm-extent-start-position extent))))
  1228.       (setq type (downcase (car (vm-mm-layout-type layout)))
  1229.         type-no-subtype (car (vm-parse type "\\([^/]+\\)")))
  1230.       (cond ((and (vm-mime-should-display-button layout dont-honor-c-d)
  1231.               (or (condition-case nil
  1232.                   (funcall (intern
  1233.                     (concat "vm-mime-display-button-"
  1234.                         type))
  1235.                        layout)
  1236.                 (void-function nil))
  1237.               (condition-case nil
  1238.                   (funcall (intern
  1239.                     (concat "vm-mime-display-button-"
  1240.                         type-no-subtype))
  1241.                        layout)
  1242.                 (void-function nil)))))
  1243.         ((and (vm-mime-should-display-internal layout dont-honor-c-d)
  1244.               (condition-case nil
  1245.                   (funcall (intern
  1246.                     (concat "vm-mime-display-internal-"
  1247.                         type))
  1248.                        layout)
  1249.                 (void-function nil))))
  1250.         ((vm-mime-types-match "multipart" type)
  1251.          (or (condition-case nil
  1252.              (funcall (intern
  1253.                    (concat "vm-mime-display-internal-"
  1254.                        type))
  1255.                   layout)
  1256.                (void-function nil))
  1257.              (vm-mime-display-internal-multipart/mixed layout)))
  1258.         ((and (vm-mime-should-display-external type)
  1259.               (vm-mime-display-external-generic layout))
  1260.          (and extent (vm-set-extent-property
  1261.                   extent 'vm-mime-disposable nil)))
  1262.         ((vm-mime-can-convert type)
  1263.          (vm-decode-mime-layout
  1264.           (vm-mime-convert-undisplayable-layout layout)))
  1265.         ((and (or (vm-mime-types-match "message" type)
  1266.               (vm-mime-types-match "text" type))
  1267.               ;; display unmatched message and text types as
  1268.               ;; text/plain.
  1269.               (vm-mime-display-internal-text/plain layout)))
  1270.         (t (and extent (vm-mime-rewrite-failed-button
  1271.                 extent
  1272.                 (or (vm-mm-layout-display-error layout)
  1273.                     "no external viewer defined for type")))
  1274.            (vm-mime-display-internal-application/octet-stream
  1275.             (or extent layout))))
  1276.       (and extent (vm-mime-delete-button-maybe extent)))
  1277.       (set-buffer-modified-p modified)))
  1278.   t )
  1279.  
  1280. (defun vm-mime-display-button-text (layout)
  1281.   (vm-mime-display-button-xxxx layout t))
  1282.  
  1283. ;;(defun vm-mime-display-internal-text/html (layout)
  1284. ;;  (if (fboundp 'w3-region)
  1285. ;;      (let ((buffer-read-only nil)
  1286. ;;        (work-buffer nil))
  1287. ;;    (message "Inlining text/html, be patient...")
  1288. ;;    ;; w3-region is not as tame as we would like.
  1289. ;;    ;; make sure the yoke is firmly attached.
  1290. ;;    (unwind-protect
  1291. ;;        (progn
  1292. ;;          (save-excursion
  1293. ;;        (set-buffer (setq work-buffer
  1294. ;;                  (generate-new-buffer " *workbuf*")))
  1295. ;;        (vm-mime-insert-mime-body layout)
  1296. ;;        (vm-mime-transfer-decode-region layout (point-min) (point-max))
  1297. ;;        (save-excursion
  1298. ;;          (save-window-excursion
  1299. ;;            (w3-region (point-min) (point-max)))))
  1300. ;;          (insert-buffer-substring work-buffer))
  1301. ;;      (and work-buffer (kill-buffer work-buffer)))
  1302. ;;    (message "Inlining text/html... done")
  1303. ;;    t )
  1304. ;;    (vm-set-mm-layout-display-error layout "Need W3 to inline HTML")
  1305. ;;    nil ))
  1306.  
  1307. (defun vm-mime-display-internal-text/plain (layout &optional no-highlighting)
  1308.   (let ((start (point)) end old-size
  1309.     (buffer-read-only nil)
  1310.     (charset (or (vm-mime-get-parameter layout "charset") "us-ascii")))
  1311.     (if (not (vm-mime-charset-internally-displayable-p charset))
  1312.     (progn
  1313.       (vm-set-mm-layout-display-error
  1314.        layout (concat "Undisplayable charset: " charset))
  1315.       nil)
  1316.       (vm-mime-insert-mime-body layout)
  1317.       (setq end (point-marker))
  1318.       (vm-mime-transfer-decode-region layout start end)
  1319.       (setq old-size (buffer-size))
  1320.       (vm-mime-charset-decode-region charset start end)
  1321.       (set-marker end (+ end (- (buffer-size) old-size)))
  1322.       (or no-highlighting (vm-energize-urls-in-message-region start end))
  1323.       (goto-char end)
  1324.       t )))
  1325.  
  1326. (defun vm-mime-display-internal-text/enriched (layout)
  1327.   (require 'enriched)
  1328.   (let ((start (point)) end
  1329.     (buffer-read-only nil)
  1330.     (enriched-verbose t))
  1331.     (message "Decoding text/enriched, be patient...")
  1332.     (vm-mime-insert-mime-body layout)
  1333.     (setq end (point-marker))
  1334.     (vm-mime-transfer-decode-region layout start end)
  1335.     ;; enriched-decode expects a couple of headers at the top of
  1336.     ;; the region and will remove anything that looks like a
  1337.     ;; header.  Put a header section here for it to eat so it
  1338.     ;; won't eat message text instead.
  1339.     (goto-char start)
  1340.     (insert "Comment: You should not see this header\n\n")
  1341.     (condition-case errdata
  1342.     (enriched-decode start end)
  1343.       (error (vm-set-mm-layout-display-error
  1344.           layout (format "enriched-decode signaled %s" errdata))))
  1345.     (vm-energize-urls-in-message-region start end)
  1346.     (goto-char end)
  1347.     (message "Decoding text/enriched... done")
  1348.     t ))
  1349.  
  1350. (defun vm-mime-display-external-generic (layout)
  1351.   (let ((program-list (vm-mime-find-external-viewer
  1352.                (car (vm-mm-layout-type layout))))
  1353.     (buffer-read-only nil)
  1354.     (start (point))
  1355.     (coding-system-for-read 'binary)
  1356.     (coding-system-for-write 'binary)
  1357.     process    tempfile cache end)
  1358.     (setq cache (cdr (assq 'vm-mime-display-external-generic
  1359.                (vm-mm-layout-cache layout)))
  1360.       process (nth 0 cache)
  1361.       tempfile (nth 1 cache))
  1362.     (if (and (processp process) (eq (process-status process) 'run))
  1363.     t
  1364.       (cond ((or (null tempfile) (null (file-exists-p tempfile)))
  1365.          (vm-mime-insert-mime-body layout)
  1366.          (setq end (point-marker))
  1367.          (vm-mime-transfer-decode-region layout start end)
  1368.          (setq tempfile (vm-make-tempfile-name))
  1369.          (let ((buffer-file-type buffer-file-type)
  1370.            buffer-file-coding-system)
  1371.            ;; Tell DOS/Windows NT whether the file is binary
  1372.            (setq buffer-file-type
  1373.              (not (vm-mime-text-type-layout-p layout)))
  1374.            ;; Tell XEmacs/MULE not to mess with the bits unless
  1375.            ;; this is a text type.
  1376.            (if vm-xemacs-mule-p
  1377.            (if (vm-mime-text-type-layout-p layout)
  1378.                (set-buffer-file-coding-system 'no-conversion nil)
  1379.              (set-buffer-file-coding-system 'binary t)))
  1380.                ;; Write an empty tempfile out to disk and set its
  1381.                ;; permissions to 0600, then write the actual buffer
  1382.                ;; contents to tempfile.
  1383.                (write-region start start tempfile nil 0)
  1384.                (set-file-modes tempfile 384)
  1385.            (write-region start end tempfile nil 0))
  1386.          (delete-region start end)
  1387.          (save-excursion
  1388.            (vm-select-folder-buffer)
  1389.            (setq vm-folder-garbage-alist
  1390.              (cons (cons tempfile 'delete-file)
  1391.                vm-folder-garbage-alist)))))
  1392.       (message "Launching %s..." (mapconcat 'identity program-list " "))
  1393.       (setq process
  1394.         (apply 'start-process
  1395.            (format "view %25s"
  1396.                (vm-mime-sprintf
  1397.                 (vm-mime-find-format-for-layout layout)
  1398.                 layout))
  1399.            nil (append program-list (list tempfile))))
  1400.       (process-kill-without-query process t)
  1401.       (message "Launching %s... done" (mapconcat 'identity
  1402.                                 program-list
  1403.                                 " "))
  1404.       (save-excursion
  1405.     (vm-select-folder-buffer)
  1406.     (setq vm-message-garbage-alist
  1407.           (cons (cons process 'delete-process)
  1408.             vm-message-garbage-alist)))
  1409.       (vm-set-mm-layout-cache
  1410.        layout
  1411.        (nconc (vm-mm-layout-cache layout)
  1412.           (list (cons 'vm-mime-display-external-generic
  1413.               (list process tempfile)))))))
  1414.   t )
  1415.  
  1416. (defun vm-mime-display-internal-application/octet-stream (layout)
  1417.   (if (vectorp layout)
  1418.       (let ((buffer-read-only nil)
  1419.         (vm-mf-default-action "save to a file"))
  1420.     (vm-mime-insert-button
  1421.      (vm-mime-sprintf (vm-mime-find-format-for-layout layout) layout)
  1422.      (function
  1423.       (lambda (layout)
  1424.         (save-excursion
  1425.           (vm-mime-display-internal-application/octet-stream layout))))
  1426.      layout nil))
  1427.     (goto-char (vm-extent-start-position layout))
  1428.     (setq layout (vm-extent-property layout 'vm-mime-layout))
  1429.     ;; support old "name" paramater for application/octet-stream
  1430.     ;; but don't override the "filename" parameter extracted from
  1431.     ;; Content-Disposition, if any.
  1432.     (let ((default-filename
  1433.         (if (vm-mime-get-disposition-parameter layout "filename")
  1434.         nil
  1435.           (vm-mime-get-parameter layout "name"))))
  1436.       (vm-mime-send-body-to-file layout default-filename)))
  1437.   t )
  1438. (fset 'vm-mime-display-button-application/octet-stream
  1439.       'vm-mime-display-internal-application/octet-stream)
  1440.  
  1441. (defun vm-mime-display-button-application (layout)
  1442.   (vm-mime-display-button-xxxx layout nil))
  1443.  
  1444. (defun vm-mime-display-button-image (layout)
  1445.   (vm-mime-display-button-xxxx layout t))
  1446.  
  1447. (defun vm-mime-display-button-audio (layout)
  1448.   (vm-mime-display-button-xxxx layout nil))
  1449.  
  1450. (defun vm-mime-display-button-video (layout)
  1451.   (vm-mime-display-button-xxxx layout t))
  1452.  
  1453. (defun vm-mime-display-button-message (layout)
  1454.   (vm-mime-display-button-xxxx layout t))
  1455.  
  1456. (defun vm-mime-display-button-multipart (layout)
  1457.   (vm-mime-display-button-xxxx layout t))
  1458.  
  1459. (defun vm-mime-display-internal-multipart/mixed (layout)
  1460.   (let ((part-list (vm-mm-layout-parts layout)))
  1461.     (while part-list
  1462.       (vm-decode-mime-layout (car part-list))
  1463.       (setq part-list (cdr part-list)))
  1464.     t ))
  1465.  
  1466. (defun vm-mime-display-internal-multipart/alternative (layout)
  1467.   (let (best-layout)
  1468.     (cond ((eq vm-mime-alternative-select-method 'best)
  1469.        (let ((done nil)
  1470.          (best nil)
  1471.          part-list type)
  1472.          (setq part-list (vm-mm-layout-parts layout)
  1473.            part-list (nreverse (copy-sequence part-list)))
  1474.          (while (and part-list (not done))
  1475.            (setq type (car (vm-mm-layout-type (car part-list))))
  1476.            (if (or (vm-mime-can-display-internal (car part-list))
  1477.                (vm-mime-find-external-viewer type))
  1478.            (setq best (car part-list)
  1479.              done t)
  1480.          (setq part-list (cdr part-list))))
  1481.          (setq best-layout (or best (car (vm-mm-layout-parts layout))))))
  1482.       ((eq vm-mime-alternative-select-method 'best-internal)
  1483.        (let ((done nil)
  1484.          (best nil)
  1485.          (second-best nil)
  1486.          part-list type)
  1487.          (setq part-list (vm-mm-layout-parts layout)
  1488.            part-list (nreverse (copy-sequence part-list)))
  1489.          (while (and part-list (not done))
  1490.            (setq type (car (vm-mm-layout-type (car part-list))))
  1491.            (cond ((vm-mime-can-display-internal (car part-list))
  1492.               (setq best (car part-list)
  1493.                 done t))
  1494.              ((and (null second-best)
  1495.                (vm-mime-find-external-viewer type))
  1496.               (setq second-best (car part-list))))
  1497.            (setq part-list (cdr part-list)))
  1498.          (setq best-layout (or best second-best
  1499.                    (car (vm-mm-layout-parts layout)))))))
  1500.   (vm-decode-mime-layout best-layout)))
  1501.  
  1502. (defun vm-mime-display-button-multipart/parallel (layout)
  1503.   (vm-mime-insert-button
  1504.    (vm-mime-sprintf (vm-mime-find-format-for-layout layout) layout)
  1505.    (function
  1506.     (lambda (layout)
  1507.       (save-excursion
  1508.     (let ((vm-auto-displayed-mime-content-types t))
  1509.       (vm-decode-mime-layout layout t)))))
  1510.    layout t))
  1511.  
  1512. (fset 'vm-mime-display-internal-multipart/parallel
  1513.       'vm-mime-display-internal-multipart/mixed)
  1514.  
  1515. (defun vm-mime-display-internal-multipart/digest (layout)
  1516.   (if (vectorp layout)
  1517.       (let ((buffer-read-only nil))
  1518.     (vm-mime-insert-button
  1519.      (vm-mime-sprintf (vm-mime-find-format-for-layout layout) layout)
  1520.      (function
  1521.       (lambda (layout)
  1522.         (save-excursion
  1523.           (vm-mime-display-internal-multipart/digest layout))))
  1524.      layout nil))
  1525.     (goto-char (vm-extent-start-position layout))
  1526.     (setq layout (vm-extent-property layout 'vm-mime-layout))
  1527.     (set-buffer (generate-new-buffer (format "digest from %s/%s"
  1528.                          (buffer-name vm-mail-buffer)
  1529.                          (vm-number-of
  1530.                           (car vm-message-pointer)))))
  1531.     (setq vm-folder-type vm-default-folder-type)
  1532.     (vm-mime-burst-layout layout nil)
  1533.     (vm-save-buffer-excursion
  1534.      (vm-goto-new-folder-frame-maybe 'folder)
  1535.      (vm-mode)
  1536.      (if (vm-should-generate-summary)
  1537.      (progn
  1538.        (vm-goto-new-summary-frame-maybe)
  1539.        (vm-summarize))))
  1540.     ;; temp buffer, don't offer to save it.
  1541.     (setq buffer-offer-save nil)
  1542.     (vm-display (or vm-presentation-buffer (current-buffer)) t
  1543.         (list this-command) '(vm-mode startup)))
  1544.   t )
  1545. (fset 'vm-mime-display-button-multipart/digest
  1546.       'vm-mime-display-internal-multipart/digest)
  1547.  
  1548. (defun vm-mime-display-button-message/rfc822 (layout)
  1549.   (let ((buffer-read-only nil))
  1550.     (vm-mime-insert-button
  1551.      (vm-mime-sprintf (vm-mime-find-format-for-layout layout) layout)
  1552.      (function
  1553.       (lambda (layout)
  1554.     (save-excursion
  1555.       (vm-mime-display-internal-message/rfc822 layout))))
  1556.      layout nil)))
  1557. (fset 'vm-mime-display-button-message/news
  1558.       'vm-mime-display-button-message/rfc822)
  1559.  
  1560. (defun vm-mime-display-internal-message/rfc822 (layout)
  1561.   (if (vectorp layout)
  1562.       (let ((start (point))
  1563.         (buffer-read-only nil))
  1564.     (vm-mime-insert-mime-headers (car (vm-mm-layout-parts layout)))
  1565.     (insert ?\n)
  1566.     (save-excursion
  1567.       (goto-char start)
  1568.       (vm-reorder-message-headers nil vm-visible-headers
  1569.                       vm-invisible-header-regexp))
  1570.     (save-restriction
  1571.       (narrow-to-region start (point))
  1572.       (vm-decode-mime-encoded-words))
  1573.     (vm-mime-display-internal-multipart/mixed layout))
  1574.     (goto-char (vm-extent-start-position layout))
  1575.     (setq layout (vm-extent-property layout 'vm-mime-layout))
  1576.     (set-buffer (generate-new-buffer
  1577.          (format "message from %s/%s"
  1578.              (buffer-name vm-mail-buffer)
  1579.              (vm-number-of
  1580.               (car vm-message-pointer)))))
  1581.     (setq vm-folder-type vm-default-folder-type)
  1582.     (vm-mime-burst-layout layout nil)
  1583.     (set-buffer-modified-p nil)
  1584.     (vm-save-buffer-excursion
  1585.      (vm-goto-new-folder-frame-maybe 'folder)
  1586.      (vm-mode)
  1587.      (if (vm-should-generate-summary)
  1588.      (progn
  1589.        (vm-goto-new-summary-frame-maybe)
  1590.        (vm-summarize))))
  1591.     ;; temp buffer, don't offer to save it.
  1592.     (setq buffer-offer-save nil)
  1593.     (vm-display (or vm-presentation-buffer (current-buffer)) t
  1594.         (list this-command) '(vm-mode startup)))
  1595.   t )
  1596. (fset 'vm-mime-display-internal-message/news
  1597.       'vm-mime-display-internal-message/rfc822)
  1598.  
  1599. (defun vm-mime-display-internal-message/partial (layout)
  1600.   (if (vectorp layout)
  1601.       (let ((buffer-read-only nil))
  1602.     (vm-mime-insert-button
  1603.      (vm-mime-sprintf (vm-mime-find-format-for-layout layout) layout)
  1604.      (function
  1605.       (lambda (layout)
  1606.         (save-excursion
  1607.           (vm-mime-display-internal-message/partial layout))))
  1608.      layout nil))
  1609.     (message "Assembling message...")
  1610.     (let ((parts nil)
  1611.       (missing nil)
  1612.       (work-buffer nil)
  1613.       extent id o number total m i prev part-header-pos
  1614.       p-id p-number p-total p-list)
  1615.       (setq extent layout
  1616.         layout (vm-extent-property extent 'vm-mime-layout)
  1617.         id (vm-mime-get-parameter layout "id"))
  1618.       (if (null id)
  1619.       (vm-mime-error
  1620.        "message/partial message missing id parameter"))
  1621.       (save-excursion
  1622.     (set-buffer (marker-buffer (vm-mm-layout-body-start layout)))
  1623.     (save-excursion
  1624.       (save-restriction
  1625.         (widen)
  1626.         (goto-char (point-min))
  1627.         (while (and (search-forward id nil t)
  1628.             (setq m (vm-message-at-point)))
  1629.           (setq o (vm-mm-layout m))
  1630.           (if (not (vectorp o))
  1631.           nil
  1632.         (setq p-list (vm-mime-find-message/partials o id))
  1633.         (while p-list
  1634.           (setq p-id (vm-mime-get-parameter (car p-list) "id"))
  1635.           (setq p-total (vm-mime-get-parameter (car p-list) "total"))
  1636.           (if (null p-total)
  1637.               nil
  1638.             (setq p-total (string-to-int p-total))
  1639.             (if (< p-total 1)
  1640.             (vm-mime-error "message/partial specified part total < 1, %d" p-total))
  1641.             (if total
  1642.             (if (not (= total p-total))
  1643.                 (vm-mime-error "message/partial specified total differs between parts, (%d != %d)" p-total total))
  1644.               (setq total p-total)))
  1645.           (setq p-number (vm-mime-get-parameter (car p-list) "number"))
  1646.           (if (null p-number)
  1647.               (vm-mime-error
  1648.                "message/partial message missing number parameter"))
  1649.           (setq p-number (string-to-int p-number))
  1650.           (if (< p-number 1)
  1651.               (vm-mime-error "message/partial part number < 1, %d"
  1652.                      p-number))
  1653.           (if (and total (> p-number total))
  1654.               (vm-mime-error "message/partial part number greater than expected number of parts, (%d > %d)" p-number total))
  1655.           (setq parts (cons (list p-number (car p-list)) parts)
  1656.             p-list (cdr p-list))))
  1657.           (goto-char (vm-mm-layout-body-end o))))))
  1658.       (if (null total)
  1659.       (vm-mime-error "total number of parts not specified in any message/partial part"))
  1660.       (setq parts (sort parts
  1661.             (function
  1662.              (lambda (p q)
  1663.                (< (car p)
  1664.                   (car q))))))
  1665.       (setq i 0
  1666.         p-list parts)
  1667.       (while p-list
  1668.     (cond ((< i (car (car p-list)))
  1669.            (vm-increment i)
  1670.            (cond ((not (= i (car (car p-list))))
  1671.               (setq missing (cons i missing)))
  1672.              (t (setq prev p-list
  1673.                   p-list (cdr p-list)))))
  1674.           (t
  1675.            ;; remove duplicate part
  1676.            (setcdr prev (cdr p-list))
  1677.            (setq p-list (cdr p-list)))))
  1678.       (while (< i total)
  1679.     (vm-increment i)
  1680.     (setq missing (cons i missing)))
  1681.       (if missing
  1682.       (vm-mime-error "part%s %s%s missing"
  1683.              (if (cdr missing) "s" "")
  1684.              (mapconcat
  1685.               (function identity)
  1686.               (nreverse (mapcar 'int-to-string
  1687.                         (or (cdr missing) missing)))
  1688.               ", ")
  1689.              (if (cdr missing)
  1690.                  (concat " and " (car missing))
  1691.                "")))
  1692.       (set-buffer (generate-new-buffer "assembled message"))
  1693.       (setq vm-folder-type vm-default-folder-type)
  1694.       (vm-mime-insert-mime-headers (car (cdr (car parts))))
  1695.       (goto-char (point-min))
  1696.       (vm-reorder-message-headers
  1697.        nil nil
  1698. "\\(Encrypted\\|Content-\\|MIME-Version\\|Message-ID\\|Subject\\|X-VM-\\|Status\\)")
  1699.       (goto-char (point-max))
  1700.       (setq part-header-pos (point))
  1701.       (while parts
  1702.     (vm-mime-insert-mime-body (car (cdr (car parts))))
  1703.     (setq parts (cdr parts)))
  1704.       (goto-char part-header-pos)
  1705.       (vm-reorder-message-headers
  1706.        nil '("Subject" "MIME-Version" "Content-" "Message-ID" "Encrypted") nil)
  1707.       (vm-munge-message-separators vm-folder-type (point-min) (point-max))
  1708.       (goto-char (point-min))
  1709.       (insert (vm-leading-message-separator))
  1710.       (goto-char (point-max))
  1711.       (insert (vm-trailing-message-separator))
  1712.       (set-buffer-modified-p nil)
  1713.       (message "Assembling message... done")
  1714.       (vm-save-buffer-excursion
  1715.        (vm-goto-new-folder-frame-maybe 'folder)
  1716.        (vm-mode)
  1717.        (if (vm-should-generate-summary)
  1718.        (progn
  1719.          (vm-goto-new-summary-frame-maybe)
  1720.          (vm-summarize))))
  1721.       ;; temp buffer, don't offer to save it.
  1722.       (setq buffer-offer-save nil)
  1723.       (vm-display (or vm-presentation-buffer (current-buffer)) t
  1724.           (list this-command) '(vm-mode startup)))
  1725.     t ))
  1726. (fset 'vm-mime-display-button-message/partial
  1727.       'vm-mime-display-internal-message/partial)
  1728.  
  1729. (defun vm-mime-display-internal-image-xxxx (layout feature name)
  1730.   (if (and vm-xemacs-p
  1731.        (featurep feature)
  1732.        (eq (device-type) 'x))
  1733.       (let ((start (point)) end tempfile g e
  1734.         (buffer-read-only nil))
  1735.     (if (setq g (cdr (assq 'vm-mime-display-internal-image-xxxx
  1736.                    (vm-mm-layout-cache layout))))
  1737.         nil
  1738.       (vm-mime-insert-mime-body layout)
  1739.       (setq end (point-marker))
  1740.       (vm-mime-transfer-decode-region layout start end)
  1741.       (setq tempfile (vm-make-tempfile-name))
  1742.       ;; Write an empty tempfile out to disk and set its
  1743.       ;; permissions to 0600, then write the actual buffer
  1744.       ;; contents to tempfile.
  1745.       (write-region start start tempfile nil 0)
  1746.       (set-file-modes tempfile 384)
  1747.       ;; coding system for presentation buffer is binary so
  1748.       ;; we don't need to set it here.
  1749.       (write-region start end tempfile nil 0)
  1750.       (message "Creating %s glyph..." name)
  1751.       (setq g (make-glyph
  1752.            (list
  1753.             (cons (list 'win)
  1754.               (vector feature ':file tempfile))
  1755.             (cons (list 'win)
  1756.               (vector 'string
  1757.                   ':data
  1758.                   (format "[Unknown/Bad %s image encoding]\n"
  1759.                       name)))
  1760.             (cons nil
  1761.               (vector 'string
  1762.                   ':data
  1763.                   (format "[%s image]\n" name))))))
  1764.       (message "")
  1765.       (vm-set-mm-layout-cache
  1766.        layout
  1767.        (nconc (vm-mm-layout-cache layout)
  1768.           (list (cons 'vm-mime-display-internal-image-xxxx g))))
  1769.       (save-excursion
  1770.         (vm-select-folder-buffer)
  1771.         (setq vm-folder-garbage-alist
  1772.           (cons (cons tempfile 'delete-file)
  1773.             vm-folder-garbage-alist)))
  1774.       (delete-region start end))
  1775.     (if (not (bolp))
  1776.         (insert-char ?\n 2)
  1777.       (insert-char ?\n 1))
  1778.     (setq e (vm-make-extent (1- (point)) (point)))
  1779.     (vm-set-extent-property e 'begin-glyph g)
  1780.     t )))
  1781.  
  1782. (defun vm-mime-display-internal-image/gif (layout)
  1783.   (vm-mime-display-internal-image-xxxx layout 'gif "GIF"))
  1784.  
  1785. (defun vm-mime-display-internal-image/jpeg (layout)
  1786.   (vm-mime-display-internal-image-xxxx layout 'jpeg "JPEG"))
  1787.  
  1788. (defun vm-mime-display-internal-image/png (layout)
  1789.   (vm-mime-display-internal-image-xxxx layout 'png "PNG"))
  1790.  
  1791. (defun vm-mime-display-internal-image/tiff (layout)
  1792.   (vm-mime-display-internal-image-xxxx layout 'tiff "TIFF"))
  1793.  
  1794. (defun vm-mime-display-internal-audio/basic (layout)
  1795.   (if (and vm-xemacs-p
  1796.        (or (featurep 'native-sound)
  1797.            (featurep 'nas-sound))
  1798.        (or (device-sound-enabled-p)
  1799.            (and (featurep 'native-sound)
  1800.             (not native-sound-only-on-console)
  1801.             (eq (device-type) 'x))))
  1802.       (let ((start (point)) end tempfile
  1803.         (buffer-read-only nil))
  1804.     (if (setq tempfile (cdr (assq 'vm-mime-display-internal-audio/basic
  1805.                       (vm-mm-layout-cache layout))))
  1806.         nil
  1807.       (vm-mime-insert-mime-body layout)
  1808.       (setq end (point-marker))
  1809.       (vm-mime-transfer-decode-region layout start end)
  1810.       (setq tempfile (vm-make-tempfile-name))
  1811.       ;; Write an empty tempfile out to disk and set its
  1812.       ;; permissions to 0600, then write the actual buffer
  1813.       ;; contents to tempfile.
  1814.       (write-region start start tempfile nil 0)
  1815.       (set-file-modes tempfile 384)
  1816.       ;; coding system for presentation buffer is binary, so
  1817.       ;; we don't need to set it here.
  1818.       (write-region start end tempfile nil 0)
  1819.       (vm-set-mm-layout-cache
  1820.        layout
  1821.        (nconc (vm-mm-layout-cache layout)
  1822.           (list (cons 'vm-mime-display-internal-audio/basic
  1823.                   tempfile))))
  1824.       (save-excursion
  1825.         (vm-select-folder-buffer)
  1826.         (setq vm-folder-garbage-alist
  1827.           (cons (cons tempfile 'delete-file)
  1828.             vm-folder-garbage-alist)))
  1829.       (delete-region start end))
  1830.     (start-itimer "audioplayer"
  1831.               (list 'lambda nil (list 'play-sound-file tempfile))
  1832.               1)
  1833.     t )
  1834.     nil ))
  1835.  
  1836. (defun vm-mime-display-button-xxxx (layout disposable)
  1837.   (vm-mime-insert-button
  1838.    (vm-mime-sprintf (vm-mime-find-format-for-layout layout) layout)
  1839.    (function
  1840.     (lambda (layout)
  1841.       (save-excursion
  1842.     (let ((vm-auto-displayed-mime-content-types t))
  1843.       (vm-decode-mime-layout layout t)))))
  1844.    layout disposable)
  1845.   t )
  1846.  
  1847. (defun vm-mime-run-display-function-at-point (&optional function dispose)
  1848.   (interactive)
  1849.   ;; save excursion to keep point from moving.  its motion would
  1850.   ;; drag window point along, to a place arbitrarily far from
  1851.   ;; where it was when the user triggered the button.
  1852.   (save-excursion
  1853.     (cond (vm-fsfemacs-p
  1854.        (let (o-list o (found nil))
  1855.          (setq o-list (overlays-at (point)))
  1856.          (while (and o-list (not found))
  1857.            (cond ((overlay-get (car o-list) 'vm-mime-layout)
  1858.               (setq found t)
  1859.               (funcall (or function (overlay-get (car o-list)
  1860.                              'vm-mime-function))
  1861.                    (car o-list))))
  1862.            (setq o-list (cdr o-list)))))
  1863.       (vm-xemacs-p
  1864.        (let ((e (extent-at (point) nil 'vm-mime-layout)))
  1865.          (funcall (or function (extent-property e 'vm-mime-function))
  1866.               e))))))
  1867.  
  1868. ;; for the karking compiler
  1869. (defvar vm-menu-mime-dispose-menu)
  1870.  
  1871. (defun vm-mime-set-extent-glyph-for-type (e type)
  1872.   (if (and vm-xemacs-p
  1873.        (featurep 'xpm)
  1874.        (eq (device-type) 'x)
  1875.        (> (device-bitplanes) 7))
  1876.       (let ((dir vm-image-directory)
  1877.         (colorful (> (device-bitplanes) 15))
  1878.         (tuples
  1879.          '(("text" "document-simple.xpm" "document-colorful.xpm")
  1880.            ("image" "mona_stamp-simple.xpm" "mona_stamp-colorful.xpm")
  1881.            ("audio" "audio_stamp-simple.xpm" "audio_stamp-colorful.xpm")
  1882.            ("video" "film-simple.xpm" "film-colorful.xpm")
  1883.            ("message" "message-simple.xpm" "message-colorful.xpm")
  1884.            ("application" "gear-simple.xpm" "gear-colorful.xpm")
  1885.            ("multipart" "stuffed_box-simple.xpm"
  1886.         "stuffed_box-colorful.xpm")))
  1887.         glyph file sym p)
  1888.     (setq file (catch 'done
  1889.              (while tuples
  1890.                (if (vm-mime-types-match (car (car tuples)) type)
  1891.                (throw 'done (car tuples))
  1892.              (setq tuples (cdr tuples))))
  1893.              nil)
  1894.           file (and file (if colorful (nth 2 file) (nth 1 file)))
  1895.           sym (and file (intern file vm-image-obarray))
  1896.           glyph (and sym (boundp sym) (symbol-value sym))
  1897.           glyph (or glyph
  1898.             (and file
  1899.                  (make-glyph
  1900.                   (list
  1901.                    (vector 'xpm ':file
  1902.                        (expand-file-name file dir))
  1903.                    [nothing])))))
  1904.     (and sym (not (boundp sym)) (set sym glyph))
  1905.     (and glyph (set-extent-begin-glyph e glyph)))))
  1906.  
  1907. (defun vm-mime-insert-button (caption action layout disposable)
  1908.   (let ((start (point))    e
  1909.     (keymap (make-sparse-keymap))
  1910.     (buffer-read-only nil))
  1911.     (if (fboundp 'set-keymap-parents)
  1912.     (if (current-local-map)
  1913.         (set-keymap-parents keymap (list (current-local-map))))
  1914.       (setq keymap (nconc keymap (current-local-map))))
  1915.     (define-key keymap "\r" 'vm-mime-run-display-function-at-point)
  1916.     (if (and (vm-mouse-xemacs-mouse-p) vm-popup-menu-on-mouse-3)
  1917.     (define-key keymap 'button3 'vm-menu-popup-mime-dispose-menu))
  1918.     (if (not (bolp))
  1919.     (insert "\n"))
  1920.     (insert caption "\n")
  1921.     ;; we must use the same interface that the vm-extent functions
  1922.     ;; use.  if they use overlays, then we call make-overlay.
  1923.     (if (eq (symbol-function 'vm-make-extent) 'make-overlay)
  1924.     ;; we MUST have the five arg make-overlay.  overlays must
  1925.     ;; advance when text is inserted at their start position or
  1926.     ;; inline text and graphics will seep into the button
  1927.     ;; overlay and then be removed when the button is removed.
  1928.     (setq e (make-overlay start (point) nil t nil))
  1929.       (setq e (make-extent start (point)))
  1930.       (set-extent-property e 'start-open t)
  1931.       (set-extent-property e 'end-open t))
  1932.     (vm-mime-set-extent-glyph-for-type e (car (vm-mm-layout-type layout)))
  1933.     ;; for emacs
  1934.     (vm-set-extent-property e 'mouse-face 'highlight)
  1935.     (vm-set-extent-property e 'local-map keymap)
  1936.     ;; for xemacs
  1937.     (vm-set-extent-property e 'highlight t)
  1938.     (vm-set-extent-property e 'keymap keymap)
  1939.     (vm-set-extent-property e 'balloon-help 'vm-mouse-3-help)
  1940.     ;; for all
  1941.     (vm-set-extent-property e 'vm-mime-disposable disposable)
  1942.     (vm-set-extent-property e 'face vm-mime-button-face)
  1943.     (vm-set-extent-property e 'vm-mime-layout layout)
  1944.     (vm-set-extent-property e 'vm-mime-function action)))
  1945.  
  1946. (defun vm-mime-rewrite-failed-button (button error-string)
  1947.   (let* ((buffer-read-only nil)
  1948.      (start (point)))
  1949.     (goto-char (vm-extent-start-position button))
  1950.     (insert (format "DISPLAY FAILED -- %s\n" error-string))
  1951.     (vm-set-extent-endpoints button start (vm-extent-end-position button))
  1952.     (delete-region (point) (vm-extent-end-position button))))
  1953.  
  1954. (defun vm-mime-send-body-to-file (layout &optional default-filename)
  1955.   (if (not (vectorp layout))
  1956.       (setq layout (vm-extent-property layout 'vm-mime-layout)))
  1957.   (or default-filename
  1958.       (setq default-filename
  1959.         (vm-mime-get-disposition-parameter layout "filename")))
  1960.   (and default-filename
  1961.        (setq default-filename (file-name-nondirectory default-filename)))
  1962.   (let ((work-buffer nil)
  1963.     ;; evade the XEmacs dialog box, yeccch.
  1964.     (use-dialog-box nil)
  1965.     (dir vm-mime-attachment-save-directory)
  1966.     (done nil)
  1967.     file)
  1968.     (while (not done)
  1969.       (setq file
  1970.         (read-file-name
  1971.          (if default-filename
  1972.          (format "Write MIME body to file (default %s): "
  1973.              default-filename)
  1974.            "Write MIME body to file: ")
  1975.          dir default-filename)
  1976.       file (expand-file-name file dir))
  1977.       (if (not (file-directory-p file))
  1978.       (setq done t)
  1979.     (if default-filename
  1980.         (message "%s is a directory" file)
  1981.       (error "%s is a directory" file))
  1982.     (sit-for 2)
  1983.     (setq dir file
  1984.           default-filename (if (string-match "/$" file)
  1985.                    (concat file default-filename)
  1986.                  (concat file "/" default-filename)))))
  1987.     (save-excursion
  1988.       (unwind-protect
  1989.       (let ((coding-system-for-read 'binary)
  1990.         (coding-system-for-write 'binary))
  1991.         (setq work-buffer (generate-new-buffer " *vm-work*"))
  1992.         (buffer-disable-undo work-buffer)
  1993.         (set-buffer work-buffer)
  1994.         ;; Tell DOS/Windows NT whether the file is binary
  1995.         (setq buffer-file-type (not (vm-mime-text-type-layout-p layout)))
  1996.         ;; Tell XEmacs/MULE not to mess with the bits unless
  1997.         ;; this is a text type.
  1998.         (if vm-xemacs-mule-p
  1999.         (if (vm-mime-text-type-layout-p layout)
  2000.             (set-buffer-file-coding-system 'no-conversion nil)
  2001.           (set-buffer-file-coding-system 'binary t)))
  2002.         (vm-mime-insert-mime-body layout)
  2003.         (vm-mime-transfer-decode-region layout (point-min) (point-max))
  2004.         (or (not (file-exists-p file))
  2005.         (y-or-n-p "File exists, overwrite? ")
  2006.         (error "Aborted"))
  2007.         (write-region (point-min) (point-max) file nil nil))
  2008.     (and work-buffer (kill-buffer work-buffer))))))
  2009.  
  2010. (defun vm-mime-pipe-body-to-command (command layout &optional discard-output)
  2011.   (if (not (vectorp layout))
  2012.       (setq layout (vm-extent-property layout 'vm-mime-layout)))
  2013.   (let ((output-buffer (if discard-output
  2014.                0
  2015.              (get-buffer-create "*Shell Command Output*")))
  2016.     (work-buffer nil))
  2017.     (save-excursion
  2018.       (if (bufferp output-buffer)
  2019.       (progn
  2020.         (set-buffer output-buffer)
  2021.         (erase-buffer)))
  2022.       (unwind-protect
  2023.       (progn
  2024.         (setq work-buffer (generate-new-buffer " *vm-work*"))
  2025.         (buffer-disable-undo work-buffer)
  2026.         (set-buffer work-buffer)
  2027.         (vm-mime-insert-mime-body layout)
  2028.         (vm-mime-transfer-decode-region layout (point-min) (point-max))
  2029.         (let ((pop-up-windows (and pop-up-windows
  2030.                        (eq vm-mutable-windows t)))
  2031.           ;; Tell DOS/Windows NT whether the input is binary
  2032.           (binary-process-input
  2033.            (not (vm-mime-text-type-layout-p layout))))
  2034.           (call-process-region (point-min) (point-max)
  2035.                    (or shell-file-name "sh")
  2036.                    nil output-buffer nil
  2037.                    shell-command-switch command)))
  2038.     (and work-buffer (kill-buffer work-buffer)))
  2039.       (if (bufferp output-buffer)
  2040.       (progn
  2041.         (set-buffer output-buffer)
  2042.         (if (not (zerop (buffer-size)))
  2043.         (vm-display output-buffer t (list this-command)
  2044.                 '(vm-pipe-message-to-command))
  2045.           (vm-display nil nil (list this-command)
  2046.               '(vm-pipe-message-to-command)))))))
  2047.   t )
  2048.  
  2049. (defun vm-mime-pipe-body-to-queried-command (layout &optional discard-output)
  2050.   (let ((command (read-string "Pipe to command: ")))
  2051.     (vm-mime-pipe-body-to-command command layout discard-output)))
  2052.  
  2053. (defun vm-mime-pipe-body-to-queried-command-discard-output (layout)
  2054.   (vm-mime-pipe-body-to-queried-command layout t))
  2055.  
  2056. (defun vm-mime-send-body-to-printer (layout)
  2057.   (vm-mime-pipe-body-to-command (mapconcat (function identity)
  2058.                        (nconc (list vm-print-command)
  2059.                           vm-print-command-switches)
  2060.                        " ")
  2061.                 layout))
  2062.  
  2063. (defun vm-mime-display-body-as-text (button)
  2064.   (let ((vm-auto-displayed-mime-content-types '("text/plain"))
  2065.     (layout (copy-sequence (vm-extent-property button 'vm-mime-layout))))
  2066.     (vm-set-extent-property button 'vm-mime-disposable t)
  2067.     (vm-set-extent-property button 'vm-mime-layout layout)
  2068.     ;; not universally correct, but close enough.
  2069.     (vm-set-mm-layout-type layout '("text/plain" "charset=us-ascii"))
  2070.     (goto-char (vm-extent-start-position button))
  2071.     (vm-decode-mime-layout button t)))
  2072.  
  2073. (defun vm-mime-display-body-using-external-viewer (button)
  2074.   (let ((layout (vm-extent-property button 'vm-mime-layout)))
  2075.     (goto-char (vm-extent-start-position button))
  2076.     (if (not (vm-mime-find-external-viewer (car (vm-mm-layout-type layout))))
  2077.     (error "No viewer defined for type %s"
  2078.            (car (vm-mm-layout-type layout)))
  2079.       (vm-mime-display-external-generic layout))))
  2080.  
  2081. (defun vm-mime-scrub-description (string)
  2082.   (let ((work-buffer nil))
  2083.       (save-excursion
  2084.        (unwind-protect
  2085.        (progn
  2086.          (setq work-buffer (generate-new-buffer " *vm-work*"))
  2087.          (buffer-disable-undo work-buffer)
  2088.          (set-buffer work-buffer)
  2089.          (insert string)
  2090.          (while (re-search-forward "[ \t\n]+" nil t)
  2091.            (replace-match " "))
  2092.          (buffer-string))
  2093.      (and work-buffer (kill-buffer work-buffer))))))
  2094.  
  2095. ;; unused
  2096. ;;(defun vm-mime-layout-description (layout)
  2097. ;;  (let ((type (car (vm-mm-layout-type layout)))
  2098. ;;    description name)
  2099. ;;    (setq description
  2100. ;;      (if (vm-mm-layout-description layout)
  2101. ;;          (vm-mime-scrub-description (vm-mm-layout-description layout))))
  2102. ;;    (concat
  2103. ;;     (if description description "")
  2104. ;;     (if description ", " "")
  2105. ;;     (cond ((vm-mime-types-match "multipart/digest" type)
  2106. ;;        (let ((n (length (vm-mm-layout-parts layout))))
  2107. ;;          (format "digest (%d message%s)" n (if (= n 1) "" "s"))))
  2108. ;;       ((vm-mime-types-match "multipart/alternative" type)
  2109. ;;        "multipart alternative")
  2110. ;;       ((vm-mime-types-match "multipart" type)
  2111. ;;        (let ((n (length (vm-mm-layout-parts layout))))
  2112. ;;          (format "multipart message (%d part%s)" n (if (= n 1) "" "s"))))
  2113. ;;       ((vm-mime-types-match "text/plain" type)
  2114. ;;        (format "plain text%s"
  2115. ;;            (let ((charset (vm-mime-get-parameter layout "charset")))
  2116. ;;              (if charset
  2117. ;;              (concat ", " charset)
  2118. ;;            ""))))
  2119. ;;       ((vm-mime-types-match "text/enriched" type)
  2120. ;;        "enriched text")
  2121. ;;       ((vm-mime-types-match "text/html" type)
  2122. ;;        "HTML")
  2123. ;;       ((vm-mime-types-match "image/gif" type)
  2124. ;;        "GIF image")
  2125. ;;       ((vm-mime-types-match "image/jpeg" type)
  2126. ;;        "JPEG image")
  2127. ;;       ((and (vm-mime-types-match "application/octet-stream" type)
  2128. ;;         (setq name (vm-mime-get-parameter layout "name"))
  2129. ;;         (save-match-data (not (string-match "^[ \t]*$" name))))
  2130. ;;        name)
  2131. ;;       (t type)))))
  2132.  
  2133. (defun vm-mime-layout-contains-type (layout type)
  2134.   (if (vm-mime-types-match type (car (vm-mm-layout-type layout)))
  2135.       layout
  2136.     (let ((p (vm-mm-layout-parts layout))
  2137.       (result nil)
  2138.       (done nil))
  2139.       (while (and p (not done))
  2140.     (if (setq result (vm-mime-layout-contains-type (car p) type))
  2141.         (setq done t)
  2142.       (setq p (cdr p))))
  2143.       result )))
  2144.  
  2145. ;; breadth first traversal
  2146. (defun vm-mime-find-digests-in-layout (layout)
  2147.   (let ((layout-list (list layout))
  2148.     layout-type
  2149.     (result nil))
  2150.     (while layout-list
  2151.       (setq layout-type (car (vm-mm-layout-type (car layout-list))))
  2152.       (cond ((string-match "^multipart/digest\\|message/\\(rfc822\\|news\\)"
  2153.                layout-type)
  2154.          (setq result (nconc result (list (car layout-list)))))
  2155.         ((vm-mime-composite-type-p layout-type)
  2156.          (setq layout-list (nconc layout-list
  2157.                       (copy-sequence
  2158.                        (vm-mm-layout-parts
  2159.                     (car layout-list)))))))
  2160.       (setq layout-list (cdr layout-list)))
  2161.     result ))
  2162.   
  2163. (defun vm-mime-plain-message-p (m)
  2164.   (save-match-data
  2165.     (let ((o (vm-mm-layout m))
  2166.       (case-fold-search t))
  2167.       (and (eq (vm-mm-encoded-header m) 'none)
  2168.        (or (not (vectorp o))
  2169.            (and (vm-mime-types-match "text/plain"
  2170.                      (car (vm-mm-layout-type o)))
  2171.             (let* ((charset (or (vm-mime-get-parameter o "charset")
  2172.                       "us-ascii")))
  2173.               (vm-string-member charset vm-mime-default-face-charsets))
  2174.             (string-match "^\\(7bit\\|8bit\\|binary\\)$"
  2175.                   (vm-mm-layout-encoding o))))))))
  2176.  
  2177. (defun vm-mime-text-type-p (type)
  2178.   (let ((case-fold-search t))
  2179.     (or (string-match "text" type) (string-match "message" type))))
  2180.  
  2181. (defun vm-mime-text-type-layout-p (layout)
  2182.   (or (vm-mime-types-match "text" (car (vm-mm-layout-type layout)))
  2183.       (vm-mime-types-match "message" (car (vm-mm-layout-type layout)))))
  2184.  
  2185. (defun vm-mime-charset-internally-displayable-p (name)
  2186.   (cond ((and vm-xemacs-mule-p (eq (device-type) 'x))
  2187.      (vm-string-assoc name vm-mime-mule-charset-to-coding-alist))
  2188.     ((vm-multiple-fonts-possible-p)
  2189.      (or (vm-string-member name vm-mime-default-face-charsets)
  2190.          (vm-string-assoc name vm-mime-charset-font-alist)))
  2191.     (t
  2192.      (vm-string-member name vm-mime-default-face-charsets))))
  2193.  
  2194. (defun vm-mime-find-message/partials (layout id)
  2195.   (let ((list nil)
  2196.     (type (vm-mm-layout-type layout)))
  2197.     (cond ((vm-mime-types-match "multipart" (car type))
  2198.        (let ((parts (vm-mm-layout-parts layout)) o)
  2199.          (while parts
  2200.            (setq o (vm-mime-find-message/partials (car parts) id))
  2201.            (if o
  2202.            (setq list (nconc o list)))
  2203.            (setq parts (cdr parts)))))
  2204.       ((vm-mime-types-match "message/partial" (car type))
  2205.        (if (equal (vm-mime-get-parameter layout "id") id)
  2206.            (setq list (cons layout list)))))
  2207.     list ))
  2208.  
  2209. (defun vm-message-at-point ()
  2210.   (let ((mp vm-message-list)
  2211.     (point (point))
  2212.     (done nil))
  2213.     (while (and mp (not done))
  2214.       (if (and (>= point (vm-start-of (car mp)))
  2215.            (<= point (vm-end-of (car mp))))
  2216.       (setq done t)
  2217.     (setq mp (cdr mp))))
  2218.     (car mp)))
  2219.  
  2220. (defun vm-mime-make-multipart-boundary ()
  2221.   (let ((boundary (make-string 10 ?a))
  2222.     (i 0))
  2223.     (random t)
  2224.     (while (< i (length boundary))
  2225.       (aset boundary i (aref vm-mime-base64-alphabet
  2226.                  (% (vm-abs (lsh (random) -8))
  2227.                 (length vm-mime-base64-alphabet))))
  2228.       (vm-increment i))
  2229.     boundary ))
  2230.  
  2231. (defun vm-mime-attach-file (file type &optional charset description)
  2232.   "Attach a file to a VM composition buffer to be sent along with the message.
  2233. The file is not inserted into the buffer and MIME encoded until
  2234. you execute vm-mail-send or vm-mail-send-and-exit.  A visible tag
  2235. indicating the existence of the attachment is placed in the
  2236. composition buffer.  You can move the attachment around or remove
  2237. it entirely with normal text editing commands.  If you remove the
  2238. attachment tag, the attachment will not be sent.
  2239.  
  2240. First argument, FILE, is the name of the file to attach.  Second
  2241. argument, TYPE, is the MIME Content-Type of the file.  Optional
  2242. third argument CHARSET is the character set of the attached
  2243. document.  This argument is only used for text types, and it is
  2244. ignored for other types.  Optional fourth argument DESCRIPTION
  2245. should be a one line description of the file.
  2246.  
  2247. When called interactively all arguments are read from the
  2248. minibuffer.
  2249.  
  2250. This command is for attaching files that do not have a MIME
  2251. header section at the top.  For files with MIME headers, you
  2252. should use vm-mime-attach-mime-file to attach such a file.  VM
  2253. will extract the content type information from the headers in
  2254. this case and not prompt you for it in the minibuffer."
  2255.   (interactive
  2256.    ;; protect value of last-command and this-command
  2257.    (let ((last-command last-command)
  2258.      (this-command this-command)
  2259.      (charset nil)
  2260.      description file default-type type)
  2261.      (if (null vm-send-using-mime)
  2262.      (error "MIME attachments disabled, set vm-send-using-mime non-nil to enable."))
  2263.      (setq file (vm-read-file-name "Attach file: " nil nil t)
  2264.        default-type (or (vm-mime-default-type-from-filename file)
  2265.                 "application/octet-stream")
  2266.        type (completing-read
  2267.          (format "Content type (default %s): "
  2268.              default-type)
  2269.          vm-mime-type-completion-alist)
  2270.        type (if (> (length type) 0) type default-type))
  2271.      (if (vm-mime-types-match "text" type)
  2272.      (setq charset (completing-read "Character set (default US-ASCII): "
  2273.                     vm-mime-charset-completion-alist)
  2274.            charset (if (> (length charset) 0) charset)))
  2275.      (setq description (read-string "One line description: "))
  2276.      (if (string-match "^[ \t]*$" description)
  2277.      (setq description nil))
  2278.      (list file type charset description)))
  2279.   (if (null vm-send-using-mime)
  2280.       (error "MIME attachments disabled, set vm-send-using-mime non-nil to enable."))
  2281.   (if (file-directory-p file)
  2282.       (error "%s is a directory, cannot attach" file))
  2283.   (if (not (file-exists-p file))
  2284.       (error "No such file: %s" file))
  2285.   (if (not (file-readable-p file))
  2286.       (error "You don't have permission to read %s" file))
  2287.   (and charset (setq charset (list (concat "charset=" charset))))
  2288.   (and description (setq description (vm-mime-scrub-description description)))
  2289.   (vm-mime-attach-object file type charset description nil))
  2290.  
  2291. (defun vm-mime-attach-mime-file (file type)
  2292.   "Attach a MIME encoded file to a VM composition buffer to be sent
  2293. along with the message.
  2294.  
  2295. The file is not inserted into the buffer until you execute
  2296. vm-mail-send or vm-mail-send-and-exit.  A visible tag indicating
  2297. the existence of the attachment is placed in the composition
  2298. buffer.  You can move the attachment around or remove it entirely
  2299. with normal text editing commands.  If you remove the attachment
  2300. tag, the attachment will not be sent.
  2301.  
  2302. The first argument, FILE, is the name of the file to attach.
  2303. When called interactively the FILE argument is read from the
  2304. minibuffer.
  2305.  
  2306. The second argument, TYPE, is the MIME Content-Type of the object.
  2307.  
  2308. This command is for attaching files that have a MIME
  2309. header section at the top.  For files without MIME headers, you
  2310. should use vm-mime-attach-file to attach such a file."
  2311.   (interactive
  2312.    ;; protect value of last-command and this-command
  2313.    (let ((last-command last-command)
  2314.      (this-command this-command)
  2315.      file type)
  2316.      (if (null vm-send-using-mime)
  2317.      (error "MIME attachments disabled, set vm-send-using-mime non-nil to enable."))
  2318.      (setq file (vm-read-file-name "Attach file: " nil nil t)
  2319.        default-type (or (vm-mime-default-type-from-filename file)
  2320.                 "application/octet-stream")
  2321.        type (completing-read
  2322.          (format "Content type (default %s): "
  2323.              default-type)
  2324.          vm-mime-type-completion-alist)
  2325.        type (if (> (length type) 0) type default-type))
  2326.      (list file type)))
  2327.   (if (null vm-send-using-mime)
  2328.       (error "MIME attachments disabled, set vm-send-using-mime non-nil to enable."))
  2329.   (if (file-directory-p file)
  2330.       (error "%s is a directory, cannot attach" file))
  2331.   (if (not (file-exists-p file))
  2332.       (error "No such file: %s" file))
  2333.   (if (not (file-readable-p file))
  2334.       (error "You don't have permission to read %s" file))
  2335.   (vm-mime-attach-object file type nil nil t))
  2336.  
  2337. (defun vm-mime-attach-object (object type params description mimed)
  2338.   (if (not (eq major-mode 'mail-mode))
  2339.       (error "Command must be used in a VM Mail mode buffer."))
  2340.   (let (start end e tag-string disposition)
  2341.     (if (< (point) (save-excursion (mail-text) (point)))
  2342.     (mail-text))
  2343.     (setq start (point)
  2344.       tag-string (format "[ATTACHMENT %s, %s]" object
  2345.                  (or type "MIME file")))
  2346.     (insert tag-string "\n")
  2347.     (setq end (1- (point)))
  2348.     (if (and (stringp object) (not mimed))
  2349.     (progn
  2350.       (if (or (vm-mime-types-match "application" type)
  2351.           (vm-mime-types-match "model" type))
  2352.           (setq disposition (list "attachment"))
  2353.         (setq disposition (list "inline")))
  2354.       (setq disposition (nconc disposition
  2355.                    (list
  2356.                     (concat "filename=\""
  2357.                         (file-name-nondirectory object)
  2358.                         "\"")))))
  2359.       (setq disposition (list "unspecified")))
  2360.     (cond (vm-fsfemacs-p
  2361.        (put-text-property start end 'front-sticky nil)
  2362.        (put-text-property start end 'rear-nonsticky t)
  2363. ;; can't be intangible because menu clicking at a position needs
  2364. ;; to set point inside the tag so that a command can access the
  2365. ;; text properties there.
  2366. ;;       (put-text-property start end 'intangible object)
  2367.        (put-text-property start end 'face vm-mime-button-face)
  2368.        (put-text-property start end 'vm-mime-type type)
  2369.        (put-text-property start end 'vm-mime-object object)
  2370.        (put-text-property start end 'vm-mime-parameters params)
  2371.        (put-text-property start end 'vm-mime-description description)
  2372.        (put-text-property start end 'vm-mime-disposition disposition)
  2373.        (put-text-property start end 'vm-mime-encoded mimed)
  2374.        (put-text-property start end 'vm-mime-object object))
  2375.       (vm-xemacs-p
  2376.        (setq e (make-extent start end))
  2377.        (vm-mime-set-extent-glyph-for-type e (or type "text/plain"))
  2378.        (set-extent-property e 'start-open t)
  2379.        (set-extent-property e 'face vm-mime-button-face)
  2380.        (set-extent-property e 'duplicable t)
  2381.        (let ((keymap (make-sparse-keymap)))
  2382.          (if vm-popup-menu-on-mouse-3
  2383.          (define-key keymap 'button3
  2384.            'vm-menu-popup-content-disposition-menu))
  2385.          (set-extent-property e 'keymap keymap)
  2386.          (set-extent-property e 'balloon-help 'vm-mouse-3-help))
  2387.        (set-extent-property e 'vm-mime-type type)
  2388.        (set-extent-property e 'vm-mime-object object)
  2389.        (set-extent-property e 'vm-mime-parameters params)
  2390.        (set-extent-property e 'vm-mime-description description)
  2391.        (set-extent-property e 'vm-mime-disposition disposition)
  2392.        (set-extent-property e 'vm-mime-encoded mimed)))))
  2393.  
  2394. (defun vm-mime-attachment-disposition-at-point ()
  2395.   (cond (vm-fsfemacs-p
  2396.      (let ((disp (get-text-property (point) 'vm-mime-disposition)))
  2397.        (intern (car disp))))
  2398.     (vm-xemacs-p
  2399.      (let* ((e (extent-at (point) nil 'vm-mime-disposition))
  2400.         (disp (extent-property e 'vm-mime-disposition)))
  2401.        (intern (car disp))))))
  2402.  
  2403. (defun vm-mime-set-attachment-disposition-at-point (sym)
  2404.   (cond (vm-fsfemacs-p
  2405.      (let ((disp (get-text-property (point) 'vm-mime-disposition)))
  2406.        (setcar disp (symbol-name sym))))
  2407.     (vm-xemacs-p
  2408.      (let* ((e (extent-at (point) nil 'vm-mime-disposition))
  2409.         (disp (extent-property e 'vm-mime-disposition)))
  2410.        (setcar disp (symbol-name sym))))))
  2411.  
  2412. (defun vm-disallow-overlay-endpoint-insertion (overlay after start end
  2413.                            &optional old-size)
  2414.   (cond ((null after) nil)
  2415.     ((= start (overlay-start overlay))
  2416.      (move-overlay overlay end (overlay-end overlay)))
  2417.     ((= start (overlay-end overlay))
  2418.      (move-overlay overlay (overlay-start overlay) start))))
  2419.  
  2420. (defun vm-mime-fake-attachment-overlays (start end)
  2421.   (let ((o-list nil)
  2422.     (done nil)
  2423.     (pos start)
  2424.     object props o)
  2425.     (save-excursion
  2426.       (save-restriction
  2427.     (narrow-to-region start end)
  2428.     (while (not done)
  2429.       (setq object (get-text-property pos 'vm-mime-object))
  2430.       (setq pos (next-single-property-change pos 'vm-mime-object))
  2431.       (or pos (setq pos (point-max) done t))
  2432.       (if object
  2433.           (progn
  2434.         (setq o (make-overlay start pos))
  2435.         (overlay-put o 'insert-in-front-hooks
  2436.                  '(vm-disallow-overlay-endpoint-insertion))
  2437.         (overlay-put o 'insert-behind-hooks
  2438.                  '(vm-disallow-overlay-endpoint-insertion))
  2439.         (setq props (text-properties-at start))
  2440.         (while props
  2441.           (overlay-put o (car props) (car (cdr props)))
  2442.           (setq props (cdr (cdr props))))
  2443.         (setq o-list (cons o o-list))))
  2444.       (setq start pos))
  2445.     o-list ))))
  2446.  
  2447. (defun vm-mime-default-type-from-filename (file)
  2448.   (let ((alist vm-mime-attachment-auto-type-alist)
  2449.     (case-fold-search t)
  2450.     (done nil))
  2451.     (while (and alist (not done))
  2452.       (if (string-match (car (car alist)) file)
  2453.       (setq done t)
  2454.     (setq alist (cdr alist))))
  2455.     (and alist (cdr (car alist)))))
  2456.  
  2457. (defun vm-remove-mail-mode-header-separator ()
  2458.   (save-excursion
  2459.     (goto-char (point-min))
  2460.     (if (re-search-forward (concat "^" mail-header-separator "$") nil t)
  2461.     (progn
  2462.       (delete-region (match-beginning 0) (match-end 0))
  2463.        t )
  2464.       nil )))
  2465.  
  2466. (defun vm-add-mail-mode-header-separator ()
  2467.   (save-excursion
  2468.     (goto-char (point-min))
  2469.     (if (re-search-forward "^$" nil t)
  2470.     (replace-match mail-header-separator t t))))
  2471.  
  2472. (defun vm-mime-transfer-encode-region (encoding beg end crlf)
  2473.   (let ((case-fold-search t)
  2474.     (armor-from (and vm-mime-composition-armor-from-lines
  2475.              (let ((case-fold-search nil))
  2476.                (save-excursion
  2477.                  (goto-char beg)
  2478.                  (re-search-forward "^From " nil t)))))
  2479.     (armor-dot (let ((case-fold-search nil))
  2480.              (save-excursion
  2481.                (goto-char beg)
  2482.                (re-search-forward "^\\.\\n" nil t)))))
  2483.     (cond ((string-match "^binary$" encoding)
  2484.        (vm-mime-base64-encode-region beg end crlf)
  2485.        (setq encoding "base64"))
  2486.       ((and (not armor-from) (not armor-dot)
  2487.         (string-match "^7bit$" encoding)) t)
  2488.       ((string-match "^base64$" encoding) t)
  2489.       ((string-match "^quoted-printable$" encoding) t)
  2490.       ((eq vm-mime-8bit-text-transfer-encoding 'quoted-printable)
  2491.        (vm-mime-qp-encode-region beg end nil armor-from)
  2492.        (setq encoding "quoted-printable"))
  2493.       ((eq vm-mime-8bit-text-transfer-encoding 'base64)
  2494.        (vm-mime-base64-encode-region beg end crlf)
  2495.        (setq encoding "base64"))
  2496.       ((or armor-from armor-dot)
  2497.        (vm-mime-qp-encode-region beg end nil armor-from)
  2498.        (setq encoding "quoted-printable")))
  2499.     (downcase encoding) ))
  2500.  
  2501. (defun vm-mime-transfer-encode-layout (layout)
  2502.   (let ((list (vm-mm-layout-parts layout))
  2503.     (type (car (vm-mm-layout-type layout)))
  2504.     (encoding "7bit")
  2505.     (vm-mime-8bit-text-transfer-encoding
  2506.      vm-mime-8bit-text-transfer-encoding))
  2507.   (cond ((vm-mime-composite-type-p type)
  2508.      ;; MIME messages of type "message" and
  2509.      ;; "multipart" are required to have a non-opaque
  2510.      ;; content transfer encoding.  This means that
  2511.      ;; if the user only wants to send out 7bit data,
  2512.      ;; then any subpart that contains 8bit data must
  2513.      ;; have an opaque (qp or base64) 8->7bit
  2514.      ;; conversion performed on it so that the
  2515.      ;; enclosing entity can use a non-opaque
  2516.      ;; encoding.
  2517.      ;;
  2518.      ;; message/partial requires a "7bit" encoding so
  2519.      ;; force 8->7 conversion in that case.
  2520.      (cond ((memq vm-mime-8bit-text-transfer-encoding
  2521.               '(quoted-printable base64))
  2522.         t)
  2523.            ((vm-mime-types-match "message/partial" type)
  2524.         (setq vm-mime-8bit-text-transfer-encoding
  2525.               'quoted-printable)))
  2526.      (while list
  2527.        (if (equal (vm-mime-transfer-encode-layout (car list)) "8bit")
  2528.            (setq encoding "8bit"))
  2529.        (setq list (cdr list))))
  2530.     (t
  2531.      (if (and (vm-mime-types-match "message/partial" type)
  2532.           (not (memq vm-mime-8bit-text-transfer-encoding
  2533.                  '(quoted-printable base64))))
  2534.         (setq vm-mime-8bit-text-transfer-encoding
  2535.               'quoted-printable))
  2536.      (setq encoding
  2537.            (vm-mime-transfer-encode-region (vm-mm-layout-encoding layout)
  2538.                            (vm-mm-layout-body-start layout)
  2539.                            (vm-mm-layout-body-end layout)
  2540.                            (vm-mime-text-type-layout-p
  2541.                         layout)))))
  2542.   (if (not (equal encoding (downcase (car (vm-mm-layout-type layout)))))
  2543.       (save-excursion
  2544.     (save-restriction
  2545.       (goto-char (vm-mm-layout-header-start layout))
  2546.       (narrow-to-region (point) (vm-mm-layout-body-start layout))
  2547.       (vm-reorder-message-headers nil nil "Content-Transfer-Encoding:")
  2548.       (if (not (equal encoding "7bit"))
  2549.           (insert "CONTENT-TRANSFER-ENCODING: " encoding "\n"))
  2550.       encoding )))))
  2551.  
  2552. (defun vm-mime-encode-composition ()
  2553.  "MIME encode the current mail composition buffer.
  2554. Attachment tags added to the buffer with vm-mime-attach-file are expanded
  2555. and the approriate content-type and boundary markup information is added."
  2556.   (interactive)
  2557.   (cond (vm-xemacs-mule-p
  2558.      (vm-mime-xemacs-encode-composition))
  2559.     (vm-xemacs-p
  2560.      (vm-mime-xemacs-encode-composition))
  2561.     (vm-fsfemacs-p
  2562.      (vm-mime-fsfemacs-encode-composition))
  2563.     (t
  2564.      (error "don't know how to MIME encode composition for %s"
  2565.         (emacs-version)))))
  2566.  
  2567. (defvar enriched-mode)
  2568.  
  2569. (defun vm-mime-xemacs-encode-composition ()
  2570.   (save-restriction
  2571.     (widen)
  2572.     (if (not (eq major-mode 'mail-mode))
  2573.     (error "Command must be used in a VM Mail mode buffer."))
  2574.     (or (null (vm-mail-mode-get-header-contents "MIME-Version:"))
  2575.     (error "Message is already MIME encoded."))
  2576.     (let ((8bit nil)
  2577.       (just-one nil)
  2578.       (boundary-positions nil)
  2579.       (enriched (and (boundp 'enriched-mode) enriched-mode))
  2580.       already-mimed layout e e-list boundary
  2581.       type encoding charset params description disposition object
  2582.       opoint-min)
  2583.       (mail-text)
  2584.       (setq e-list (extent-list nil (point) (point-max))
  2585.         e-list (vm-delete (function
  2586.                    (lambda (e)
  2587.                  (extent-property e 'vm-mime-object)))
  2588.                   e-list t)
  2589.         e-list (sort e-list (function
  2590.                  (lambda (e1 e2)
  2591.                    (< (extent-end-position e1)
  2592.                       (extent-end-position e2))))))
  2593.       ;; If there's just one attachment and no other readable
  2594.       ;; text in the buffer then make the message type just be
  2595.       ;; the attachment type rather than sending a multipart
  2596.       ;; message with one attachment
  2597.       (setq just-one (and (= (length e-list) 1)
  2598.               (looking-at "[ \t\n]*")
  2599.               (= (match-end 0)
  2600.                  (extent-start-position (car e-list)))
  2601.               (save-excursion
  2602.                 (goto-char (extent-end-position (car e-list)))
  2603.                 (looking-at "[ \t\n]*\\'"))))
  2604.       (if (null e-list)
  2605.       (progn
  2606.         (narrow-to-region (point) (point-max))
  2607.         ;; support enriched-mode for text/enriched composition
  2608.         (if enriched
  2609.         (let ((enriched-initial-annotation ""))
  2610.           (enriched-encode (point-min) (point-max))))
  2611.         (setq charset (vm-determine-proper-charset (point-min)
  2612.                                (point-max)))
  2613.         (if vm-xemacs-mule-p
  2614.         (encode-coding-region (point-min) (point-max)
  2615.                       buffer-file-coding-system))
  2616.         (setq encoding (vm-determine-proper-content-transfer-encoding
  2617.                 (point-min)
  2618.                 (point-max))
  2619.           encoding (vm-mime-transfer-encode-region encoding
  2620.                                (point-min)
  2621.                                (point-max)
  2622.                                t))
  2623.         (widen)
  2624.         (vm-remove-mail-mode-header-separator)
  2625.         (goto-char (point-min))
  2626.         (vm-reorder-message-headers
  2627.          nil nil "\\(Content-Type:\\|Content-Transfer-Encoding\\|MIME-Version:\\)")
  2628.         (insert "MIME-Version: 1.0\n")
  2629.         (if enriched
  2630.         (insert "Content-Type: text/enriched; charset=" charset "\n")
  2631.           (insert "Content-Type: text/plain; charset=" charset "\n"))
  2632.         (insert "Content-Transfer-Encoding: " encoding "\n")
  2633.         (vm-add-mail-mode-header-separator))
  2634.     (while e-list
  2635.       (setq e (car e-list))
  2636.       (if (or just-one (= (point) (extent-start-position e)))
  2637.           nil
  2638.         (narrow-to-region (point) (extent-start-position e))
  2639.         (if enriched
  2640.         (let ((enriched-initial-annotation ""))
  2641.           (enriched-encode (point-min) (point-max))))
  2642.         (setq charset (vm-determine-proper-charset (point-min)
  2643.                                (point-max)))
  2644.         (if vm-xemacs-mule-p
  2645.         (encode-coding-region (point-min) (point-max)
  2646.                       buffer-file-coding-system))
  2647.         (setq encoding (vm-determine-proper-content-transfer-encoding
  2648.                 (point-min)
  2649.                 (point-max))
  2650.           encoding (vm-mime-transfer-encode-region encoding
  2651.                                (point-min)
  2652.                                (point-max)
  2653.                                t))
  2654.         (setq boundary-positions (cons (point-marker) boundary-positions))
  2655.         (if enriched
  2656.         (insert "Content-Type: text/enriched; charset=" charset "\n")
  2657.           (insert "Content-Type: text/plain; charset=" charset "\n"))
  2658.         (insert "Content-Transfer-Encoding: " encoding "\n\n")
  2659.         (widen))
  2660.       (goto-char (extent-start-position e))
  2661.       (narrow-to-region (point) (point))
  2662.       (setq object (extent-property e 'vm-mime-object))
  2663.       ;; insert the object
  2664.       (cond ((bufferp object)
  2665.          (insert-buffer-substring object))
  2666.         ((stringp object)
  2667.          (let ((coding-system-for-read
  2668.             (if (vm-mime-text-type-p
  2669.                  (extent-property e 'vm-mime-type))
  2670.                 'no-conversion
  2671.               'binary))
  2672.                ;; don't let buffer-file-coding-system be changed
  2673.                ;; by insert-file-contents-literally.  The
  2674.                ;; value we bind to it to here isn't important.
  2675.                (buffer-file-coding-system 'binary))
  2676.            (insert-file-contents-literally object))))
  2677.       ;; gather information about the object from the extent.
  2678.       (if (setq already-mimed (extent-property e 'vm-mime-encoded))
  2679.           (setq layout (vm-mime-parse-entity
  2680.                 nil (list "text/plain" "charset=us-ascii")
  2681.                 "7bit")
  2682.             type (or (extent-property e 'vm-mime-type)
  2683.                  (car (vm-mm-layout-type layout)))
  2684.             params (or (extent-property e 'vm-mime-parameters)
  2685.                    (cdr (vm-mm-layout-qtype layout)))
  2686.             description (extent-property e 'vm-mime-description)
  2687.             disposition
  2688.               (if (not
  2689.                (equal
  2690.                 (car (extent-property e 'vm-mime-disposition))
  2691.                 "unspecified"))
  2692.               (extent-property e 'vm-mime-disposition)
  2693.             (vm-mm-layout-qdisposition layout)))
  2694.         (setq type (extent-property e 'vm-mime-type)
  2695.           params (extent-property e 'vm-mime-parameters)
  2696.           description (extent-property e 'vm-mime-description)
  2697.           disposition
  2698.             (if (not (equal
  2699.                   (car (extent-property e 'vm-mime-disposition))
  2700.                   "unspecified"))
  2701.             (extent-property e 'vm-mime-disposition)
  2702.               nil)))
  2703.       (cond ((vm-mime-types-match "text" type)
  2704.          (setq encoding
  2705.                (vm-determine-proper-content-transfer-encoding
  2706.             (if already-mimed
  2707.                 (vm-mm-layout-body-start layout)
  2708.               (point-min))
  2709.             (point-max))
  2710.                encoding (vm-mime-transfer-encode-region
  2711.                  encoding
  2712.                  (if already-mimed
  2713.                      (vm-mm-layout-body-start layout)
  2714.                    (point-min))
  2715.                  (point-max)
  2716.                  t))
  2717.          (setq 8bit (or 8bit (equal encoding "8bit"))))
  2718.         ((vm-mime-composite-type-p type)
  2719.          (setq opoint-min (point-min))
  2720.          (if (not already-mimed)
  2721.              (setq layout (vm-mime-parse-entity
  2722.                    nil (list "text/plain" "charset=us-ascii")
  2723.                    "7bit")))
  2724.          (setq encoding (vm-mime-transfer-encode-layout layout))
  2725.          (setq 8bit (or 8bit (equal encoding "8bit")))
  2726.          (goto-char (point-max))
  2727.          (widen)
  2728.          (narrow-to-region opoint-min (point)))
  2729.         (t
  2730.          (vm-mime-base64-encode-region
  2731.           (if already-mimed
  2732.               (vm-mm-layout-body-start layout)
  2733.             (point-min))
  2734.           (point-max))
  2735.          (setq encoding "base64")))
  2736.       (if just-one
  2737.           nil
  2738.         (goto-char (point-min))
  2739.         (setq boundary-positions (cons (point-marker) boundary-positions))
  2740.         (if (not already-mimed)
  2741.         nil
  2742.           ;; trim headers
  2743.           (vm-reorder-message-headers
  2744.            nil (nconc (list "Content-Disposition:" "Content-ID:")
  2745.               (if description
  2746.                   (list "Content-Description:")
  2747.                 nil))
  2748.            nil)
  2749.           ;; remove header/text separator
  2750.           (goto-char (1- (vm-mm-layout-body-start layout)))
  2751.           (if (looking-at "\n")
  2752.           (delete-char 1)))
  2753.         (insert "Content-Type: " type)
  2754.         (if params
  2755.         (if vm-mime-avoid-folding-content-type
  2756.             (insert "; " (mapconcat 'identity params "; ") "\n")
  2757.           (insert ";\n\t" (mapconcat 'identity params ";\n\t") "\n"))
  2758.           (insert "\n"))
  2759.         (and description
  2760.          (insert "Content-Description: " description "\n"))
  2761.         (if disposition
  2762.         (progn
  2763.           (insert "Content-Disposition: " (car disposition))
  2764.           (if (cdr disposition)
  2765.               (insert ";\n\t" (mapconcat 'identity
  2766.                          (cdr disposition)
  2767.                          ";\n\t")))
  2768.           (insert "\n")))
  2769.         (insert "Content-Transfer-Encoding: " encoding "\n\n"))
  2770.       (goto-char (point-max))
  2771.       (widen)
  2772.       (save-excursion
  2773.         (goto-char (extent-start-position e))
  2774.         (vm-assert (looking-at "\\[ATTACHMENT")))
  2775.       (delete-region (extent-start-position e)
  2776.              (extent-end-position e))
  2777.       (detach-extent e)
  2778.       (if (looking-at "\n")
  2779.           (delete-char 1))
  2780.       (setq e-list (cdr e-list)))
  2781.     ;; handle the remaining chunk of text after the last
  2782.     ;; extent, if any.
  2783.     (if (or just-one (= (point) (point-max)))
  2784.         nil
  2785.       (if enriched
  2786.           (let ((enriched-initial-annotation ""))
  2787.         (enriched-encode (point) (point-max))))
  2788.       (setq charset (vm-determine-proper-charset (point)
  2789.                              (point-max)))
  2790.       (if vm-xemacs-mule-p
  2791.           (encode-coding-region (point) (point-max)
  2792.                     buffer-file-coding-system))
  2793.       (setq encoding (vm-determine-proper-content-transfer-encoding
  2794.               (point)
  2795.               (point-max))
  2796.         encoding (vm-mime-transfer-encode-region encoding
  2797.                              (point)
  2798.                              (point-max)
  2799.                              t))
  2800.       (setq 8bit (or 8bit (equal encoding "8bit")))
  2801.       (setq boundary-positions (cons (point-marker) boundary-positions))
  2802.       (if enriched
  2803.           (insert "Content-Type: text/enriched; charset=" charset "\n")
  2804.         (insert "Content-Type: text/plain; charset=" charset "\n"))
  2805.       (insert "Content-Transfer-Encoding: " encoding "\n\n")
  2806.       (goto-char (point-max)))
  2807.     (setq boundary (vm-mime-make-multipart-boundary))
  2808.     (mail-text)
  2809.     (while (re-search-forward (concat "^--"
  2810.                       (regexp-quote boundary)
  2811.                       "\\(--\\)?$")
  2812.                   nil t)
  2813.       (setq boundary (vm-mime-make-multipart-boundary))
  2814.       (mail-text))
  2815.     (goto-char (point-max))
  2816.     (or just-one (insert "\n--" boundary "--\n"))
  2817.     (while boundary-positions
  2818.       (goto-char (car boundary-positions))
  2819.       (insert "\n--" boundary "\n")
  2820.       (setq boundary-positions (cdr boundary-positions)))
  2821.     (if (and just-one already-mimed)
  2822.         (progn
  2823.           (goto-char (vm-mm-layout-header-start layout))
  2824.           ;; trim headers
  2825.           (vm-reorder-message-headers
  2826.            nil '("Content-Description:" "Content-ID:") nil)
  2827.           ;; remove header/text separator
  2828.           (goto-char (1- (vm-mm-layout-body-start layout)))
  2829.           (if (looking-at "\n")
  2830.           (delete-char 1))
  2831.           ;; copy remainder to enclosing entity's header section
  2832.           (insert-buffer-substring (current-buffer)
  2833.                        (vm-mm-layout-header-start layout)
  2834.                        (vm-mm-layout-body-start layout))
  2835.           (delete-region (vm-mm-layout-header-start layout)
  2836.                  (vm-mm-layout-body-start layout))))
  2837.     (goto-char (point-min))
  2838.     (vm-remove-mail-mode-header-separator)
  2839.     (vm-reorder-message-headers
  2840.      nil nil "\\(Content-Type:\\|MIME-Version:\\|Content-Transfer-Encoding\\)")
  2841.     (vm-add-mail-mode-header-separator)
  2842.     (insert "MIME-Version: 1.0\n")
  2843.     (if (not just-one)
  2844.         (insert (if vm-mime-avoid-folding-content-type
  2845.             "Content-Type: multipart/mixed; boundary=\""
  2846.               "Content-Type: multipart/mixed;\n\tboundary=\"")
  2847.             boundary "\"\n")
  2848.       (insert "Content-Type: " type)
  2849.       (if params
  2850.           (if vm-mime-avoid-folding-content-type
  2851.           (insert "; " (mapconcat 'identity params "; ") "\n")
  2852.         (insert ";\n\t" (mapconcat 'identity params ";\n\t") "\n"))
  2853.         (insert "\n")))
  2854.     (if just-one
  2855.         (and description
  2856.          (insert "Content-Description: " description "\n")))
  2857.     (if (and just-one disposition)
  2858.         (progn
  2859.           (insert "Content-Disposition: " (car disposition))
  2860.           (if (cdr disposition)
  2861.           (if vm-mime-avoid-folding-content-type
  2862.               (insert "; " (mapconcat 'identity (cdr disposition) "; ")
  2863.                   "\n")
  2864.             (insert ";\n\t" (mapconcat 'identity (cdr disposition)
  2865.                            ";\n\t")))
  2866.         (insert "\n"))))
  2867.     (if just-one
  2868.         (insert "Content-Transfer-Encoding: " encoding "\n")
  2869.       (if 8bit
  2870.           (insert "Content-Transfer-Encoding: 8bit\n")
  2871.         (insert "Content-Transfer-Encoding: 7bit\n")))))))
  2872.  
  2873. (defun vm-mime-fsfemacs-encode-composition ()
  2874.   (save-restriction
  2875.     (widen)
  2876.     (if (not (eq major-mode 'mail-mode))
  2877.     (error "Command must be used in a VM Mail mode buffer."))
  2878.     (or (null (vm-mail-mode-get-header-contents "MIME-Version:"))
  2879.     (error "Message is already MIME encoded."))
  2880.     (let ((8bit nil)
  2881.       (just-one nil)
  2882.       (boundary-positions nil)
  2883.       (enriched (and (boundp 'enriched-mode) enriched-mode))
  2884.       already-mimed layout o o-list boundary
  2885.       type encoding charset params description disposition object
  2886.       opoint-min)
  2887.       (mail-text)
  2888.       (setq o-list (vm-mime-fake-attachment-overlays (point) (point-max))
  2889.         o-list (vm-delete (function
  2890.                    (lambda (o)
  2891.                  (overlay-get o 'vm-mime-object)))
  2892.                   o-list t)
  2893.         o-list (sort o-list (function
  2894.                  (lambda (e1 e2)
  2895.                    (< (overlay-end e1)
  2896.                       (overlay-end e2))))))
  2897.       ;; If there's just one attachment and no other readable
  2898.       ;; text in the buffer then make the message type just be
  2899.       ;; the attachment type rather than sending a multipart
  2900.       ;; message with one attachment
  2901.       (setq just-one (and (= (length o-list) 1)
  2902.               (looking-at "[ \t\n]*")
  2903.               (= (match-end 0)
  2904.                  (overlay-start (car o-list)))
  2905.               (save-excursion
  2906.                 (goto-char (overlay-end (car o-list)))
  2907.                 (looking-at "[ \t\n]*\\'"))))
  2908.       (if (null o-list)
  2909.       (progn
  2910.         (narrow-to-region (point) (point-max))
  2911.         ;; support enriched-mode for text/enriched composition
  2912.         (if enriched
  2913.         (let ((enriched-initial-annotation ""))
  2914.           (enriched-encode (point-min) (point-max))))
  2915.         (setq charset (vm-determine-proper-charset (point-min)
  2916.                                (point-max)))
  2917.         (if vm-fsfemacs-mule-p
  2918.         (encode-coding-region (point-min) (point-max)
  2919.                       buffer-file-coding-system))
  2920.         (setq encoding (vm-determine-proper-content-transfer-encoding
  2921.                 (point-min)
  2922.                 (point-max))
  2923.           encoding (vm-mime-transfer-encode-region encoding
  2924.                                (point-min)
  2925.                                (point-max)
  2926.                                t))
  2927.         (widen)
  2928.         (vm-remove-mail-mode-header-separator)
  2929.         (goto-char (point-min))
  2930.         (vm-reorder-message-headers
  2931.          nil nil "\\(Content-Type:\\|Content-Transfer-Encoding\\|MIME-Version:\\)")
  2932.         (insert "MIME-Version: 1.0\n")
  2933.         (if enriched
  2934.         (insert "Content-Type: text/enriched; charset=" charset "\n")
  2935.           (insert "Content-Type: text/plain; charset=" charset "\n"))
  2936.         (insert "Content-Transfer-Encoding: " encoding "\n")
  2937.         (vm-add-mail-mode-header-separator))
  2938.     (while o-list
  2939.       (setq o (car o-list))
  2940.       (if (or just-one (= (point) (overlay-start o)))
  2941.           nil
  2942.         (narrow-to-region (point) (overlay-start o))
  2943.         ;; support enriched-mode for text/enriched composition
  2944.         (if enriched
  2945.         (let ((enriched-initial-annotation ""))
  2946.           (save-excursion
  2947.             ;; insert/delete trick needed to avoid
  2948.             ;; enriched-mode tags from seeping into the
  2949.             ;; attachment overlays.  I really wish
  2950.             ;; front-advance / rear-advance overlay
  2951.             ;; endpoint properties actually worked.
  2952.             (goto-char (point-max))
  2953.             (insert-before-markers "\n")
  2954.             (enriched-encode (point-min) (1- (point)))
  2955.             (goto-char (point-max))
  2956.             (delete-char -1))))
  2957.         (setq charset (vm-determine-proper-charset (point-min)
  2958.                                (point-max)))
  2959.         (if vm-fsfemacs-mule-p
  2960.         (encode-coding-region (point-min) (point-max)
  2961.                       buffer-file-coding-system))
  2962.         (setq encoding (vm-determine-proper-content-transfer-encoding
  2963.                 (point-min)
  2964.                 (point-max))
  2965.           encoding (vm-mime-transfer-encode-region encoding
  2966.                                (point-min)
  2967.                                (point-max)
  2968.                                t))
  2969.         (setq boundary-positions (cons (point-marker) boundary-positions))
  2970.         (if enriched
  2971.         (insert "Content-Type: text/enriched; charset=" charset "\n")
  2972.           (insert "Content-Type: text/plain; charset=" charset "\n"))
  2973.         (insert "Content-Transfer-Encoding: " encoding "\n\n")
  2974.         (widen))
  2975.       (goto-char (overlay-start o))
  2976.       (narrow-to-region (point) (point))
  2977.       (setq object (overlay-get o 'vm-mime-object))
  2978.       ;; insert the object
  2979.       (cond ((bufferp object)
  2980.          ;; as of FSF Emacs 19.34, even with the hooks
  2981.          ;; we've attached to the attachment overlays,
  2982.          ;; text STILL can be inserted into them when
  2983.          ;; font-lock is enabled.  Explaining why is
  2984.          ;; beyond the scope of this comment and I
  2985.          ;; don't know the answer anyway.  This works
  2986.          ;; to prevent it.
  2987.          (insert-before-markers " ")
  2988.          (forward-char -1)
  2989.          (insert-buffer-substring object)
  2990.          (delete-char 1))
  2991.         ((stringp object)
  2992.          (insert-before-markers " ")
  2993.          (forward-char -1)
  2994.          (let ((coding-system-for-read
  2995.             (if (vm-mime-text-type-p
  2996.                  (overlay-get o 'vm-mime-type))
  2997.                 'no-conversion
  2998.               'binary))
  2999.                ;; don't let buffer-file-coding-system be
  3000.                ;; changed by insert-file-contents.  The
  3001.                ;; value we bind to it to here isn't
  3002.                ;; important.
  3003.                (buffer-file-coding-system 'binary))
  3004.            (insert-file-contents object))
  3005.          (goto-char (point-max))
  3006.          (delete-char -1)))
  3007.       ;; gather information about the object from the extent.
  3008.       (if (setq already-mimed (overlay-get o 'vm-mime-encoded))
  3009.           (setq layout (vm-mime-parse-entity
  3010.                 nil (list "text/plain" "charset=us-ascii")
  3011.                 "7bit")
  3012.             type (or (overlay-get o 'vm-mime-type)
  3013.                  (car (vm-mm-layout-type layout)))
  3014.             params (or (overlay-get o 'vm-mime-parameters)
  3015.                    (cdr (vm-mm-layout-qtype layout)))
  3016.             description (overlay-get o 'vm-mime-description)
  3017.             disposition
  3018.               (if (not
  3019.                (equal
  3020.                 (car (overlay-get o 'vm-mime-disposition))
  3021.                 "unspecified"))
  3022.               (overlay-get o 'vm-mime-disposition)
  3023.             (vm-mm-layout-qdisposition layout)))
  3024.         (setq type (overlay-get o 'vm-mime-type)
  3025.           params (overlay-get o 'vm-mime-parameters)
  3026.           description (overlay-get o 'vm-mime-description)
  3027.           disposition
  3028.             (if (not (equal
  3029.                   (car (overlay-get o 'vm-mime-disposition))
  3030.                   "unspecified"))
  3031.             (overlay-get o 'vm-mime-disposition)
  3032.               nil)))
  3033.       (cond ((vm-mime-types-match "text" type)
  3034.          (setq encoding
  3035.                (vm-determine-proper-content-transfer-encoding
  3036.             (if already-mimed
  3037.                 (vm-mm-layout-body-start layout)
  3038.               (point-min))
  3039.             (point-max))
  3040.                encoding (vm-mime-transfer-encode-region
  3041.                  encoding
  3042.                  (if already-mimed
  3043.                      (vm-mm-layout-body-start layout)
  3044.                    (point-min))
  3045.                  (point-max)
  3046.                  t))
  3047.          (setq 8bit (or 8bit (equal encoding "8bit"))))
  3048.         ((vm-mime-composite-type-p type)
  3049.          (setq opoint-min (point-min))
  3050.          (if (not already-mimed)
  3051.              (setq layout (vm-mime-parse-entity
  3052.                    nil (list "text/plain" "charset=us-ascii")
  3053.                    "7bit")))
  3054.          (setq encoding (vm-mime-transfer-encode-layout layout))
  3055.          (setq 8bit (or 8bit (equal encoding "8bit")))
  3056.          (goto-char (point-max))
  3057.          (widen)
  3058.          (narrow-to-region opoint-min (point)))
  3059.         (t
  3060.          (vm-mime-base64-encode-region
  3061.           (if already-mimed
  3062.               (vm-mm-layout-body-start layout)
  3063.             (point-min))
  3064.           (point-max))
  3065.          (setq encoding "base64")))
  3066.       (if just-one
  3067.           nil
  3068.         (goto-char (point-min))
  3069.         (setq boundary-positions (cons (point-marker) boundary-positions))
  3070.         (if (not already-mimed)
  3071.         nil
  3072.           ;; trim headers
  3073.           (vm-reorder-message-headers
  3074.            nil (nconc (list "Content-Disposition:" "Content-ID:")
  3075.               (if description
  3076.                   (list "Content-Description:")
  3077.                 nil))
  3078.            nil)
  3079.           ;; remove header/text separator
  3080.           (goto-char (1- (vm-mm-layout-body-start layout)))
  3081.           (if (looking-at "\n")
  3082.           (delete-char 1)))
  3083.         (insert "Content-Type: " type)
  3084.         (if params
  3085.         (if vm-mime-avoid-folding-content-type
  3086.             (insert "; " (mapconcat 'identity params "; ") "\n")
  3087.           (insert ";\n\t" (mapconcat 'identity params ";\n\t") "\n"))
  3088.           (insert "\n"))
  3089.         (and description
  3090.          (insert "Content-Description: " description "\n"))
  3091.         (if disposition
  3092.         (progn
  3093.           (insert "Content-Disposition: " (car disposition))
  3094.           (if (cdr disposition)
  3095.               (insert ";\n\t" (mapconcat 'identity
  3096.                          (cdr disposition)
  3097.                          ";\n\t")))
  3098.           (insert "\n")))
  3099.         (insert "Content-Transfer-Encoding: " encoding "\n\n"))
  3100.       (goto-char (point-max))
  3101.       (widen)
  3102.       (save-excursion
  3103.         (goto-char (overlay-start o))
  3104.         (vm-assert (looking-at "\\[ATTACHMENT")))
  3105.       (delete-region (overlay-start o)
  3106.              (overlay-end o))
  3107.       (delete-overlay o)
  3108.       (if (looking-at "\n")
  3109.           (delete-char 1))
  3110.       (setq o-list (cdr o-list)))
  3111.     ;; handle the remaining chunk of text after the last
  3112.     ;; extent, if any.
  3113.     (if (or just-one (= (point) (point-max)))
  3114.         nil
  3115.       ;; support enriched-mode for text/enriched composition
  3116.       (if enriched
  3117.           (let ((enriched-initial-annotation ""))
  3118.         (enriched-encode (point) (point-max))))
  3119.       (setq charset (vm-determine-proper-charset (point)
  3120.                              (point-max)))
  3121.       (if vm-fsfemacs-mule-p
  3122.           (encode-coding-region (point-min) (point-max)
  3123.                     buffer-file-coding-system))
  3124.       (setq encoding (vm-determine-proper-content-transfer-encoding
  3125.               (point)
  3126.               (point-max))
  3127.         encoding (vm-mime-transfer-encode-region encoding
  3128.                              (point)
  3129.                              (point-max)
  3130.                              t))
  3131.       (setq 8bit (or 8bit (equal encoding "8bit")))
  3132.       (setq boundary-positions (cons (point-marker) boundary-positions))
  3133.       (if enriched
  3134.           (insert "Content-Type: text/enriched; charset=" charset "\n")
  3135.         (insert "Content-Type: text/plain; charset=" charset "\n"))
  3136.       (insert "Content-Transfer-Encoding: " encoding "\n\n")
  3137.       (goto-char (point-max)))
  3138.     (setq boundary (vm-mime-make-multipart-boundary))
  3139.     (mail-text)
  3140.     (while (re-search-forward (concat "^--"
  3141.                       (regexp-quote boundary)
  3142.                       "\\(--\\)?$")
  3143.                   nil t)
  3144.       (setq boundary (vm-mime-make-multipart-boundary))
  3145.       (mail-text))
  3146.     (goto-char (point-max))
  3147.     (or just-one (insert "\n--" boundary "--\n"))
  3148.     (while boundary-positions
  3149.       (goto-char (car boundary-positions))
  3150.       (insert "\n--" boundary "\n")
  3151.       (setq boundary-positions (cdr boundary-positions)))
  3152.     (if (and just-one already-mimed)
  3153.         (progn
  3154.           (goto-char (vm-mm-layout-header-start layout))
  3155.           ;; trim headers
  3156.           (vm-reorder-message-headers
  3157.            nil '("Content-Description:" "Content-ID:") nil)
  3158.           ;; remove header/text separator
  3159.           (goto-char (1- (vm-mm-layout-body-start layout)))
  3160.           (if (looking-at "\n")
  3161.           (delete-char 1))
  3162.           ;; copy remainder to enclosing entity's header section
  3163.           (insert-buffer-substring (current-buffer)
  3164.                        (vm-mm-layout-header-start layout)
  3165.                        (vm-mm-layout-body-start layout))
  3166.           (delete-region (vm-mm-layout-header-start layout)
  3167.                  (vm-mm-layout-body-start layout))))
  3168.     (goto-char (point-min))
  3169.     (vm-remove-mail-mode-header-separator)
  3170.     (vm-reorder-message-headers
  3171.      nil nil "\\(Content-Type:\\|MIME-Version:\\|Content-Transfer-Encoding\\)")
  3172.     (vm-add-mail-mode-header-separator)
  3173.     (insert "MIME-Version: 1.0\n")
  3174.     (if (not just-one)
  3175.         (insert (if vm-mime-avoid-folding-content-type
  3176.             "Content-Type: multipart/mixed; boundary=\""
  3177.               "Content-Type: multipart/mixed;\n\tboundary=\"")
  3178.             boundary "\"\n")
  3179.       (insert "Content-Type: " type)
  3180.       (if params
  3181.           (if vm-mime-avoid-folding-content-type
  3182.           (insert "; " (mapconcat 'identity params "; ") "\n")
  3183.         (insert ";\n\t" (mapconcat 'identity params ";\n\t") "\n"))
  3184.         (insert "\n")))
  3185.     (if just-one
  3186.         (and description
  3187.          (insert "Content-Description: " description "\n")))
  3188.     (if (and just-one disposition)
  3189.         (progn
  3190.           (insert "Content-Disposition: " (car disposition))
  3191.           (if (cdr disposition)
  3192.           (if vm-mime-avoid-folding-content-type
  3193.               (insert "; " (mapconcat 'identity (cdr disposition) "; ")
  3194.                   "\n")
  3195.             (insert ";\n\t" (mapconcat 'identity (cdr disposition)
  3196.                            ";\n\t")))
  3197.         (insert "\n"))))
  3198.     (if just-one
  3199.         (insert "Content-Transfer-Encoding: " encoding "\n")
  3200.       (if 8bit
  3201.           (insert "Content-Transfer-Encoding: 8bit\n")
  3202.         (insert "Content-Transfer-Encoding: 7bit\n")))))))
  3203.  
  3204. (defun vm-mime-fragment-composition (size)
  3205.   (save-restriction
  3206.     (widen)
  3207.     (message "Fragmenting message...")
  3208.     (let ((buffers nil)
  3209.       (total-markers nil)
  3210.       (id (vm-mime-make-multipart-boundary))
  3211.       (n 1)
  3212.       b header-start header-end master-buffer start end)
  3213.       (vm-remove-mail-mode-header-separator)
  3214.       ;; message/partial must have "7bit" content transfer
  3215.       ;; encoding, so force everything to be encoded for
  3216.       ;; 7bit transmission.
  3217.       (let ((vm-mime-8bit-text-transfer-encoding
  3218.          (if (eq vm-mime-8bit-text-transfer-encoding '8bit)
  3219.          'quoted-printable
  3220.            vm-mime-8bit-text-transfer-encoding)))
  3221.     (vm-mime-transfer-encode-layout
  3222.      (vm-mime-parse-entity nil (list "text/plain" "charset=us-ascii")
  3223.                    "7bit")))
  3224.       (goto-char (point-min))
  3225.       (setq header-start (point))
  3226.       (search-forward "\n\n")
  3227.       (setq header-end (1- (point)))
  3228.       (setq master-buffer (current-buffer))
  3229.       (goto-char (point-min))
  3230.       (setq start (point))
  3231.       (while (not (eobp))
  3232.     (condition-case nil
  3233.         (progn
  3234.           (forward-char (max (- size 150) 2000))
  3235.           (beginning-of-line))
  3236.       (end-of-buffer nil))
  3237.     (setq end (point))
  3238.     (setq b (generate-new-buffer (concat (buffer-name) " part "
  3239.                          (int-to-string n))))
  3240.     (setq buffers (cons b buffers))
  3241.     (set-buffer b)
  3242.     (make-local-variable 'vm-send-using-mime)
  3243.     (setq vm-send-using-mime nil)
  3244.     (insert-buffer-substring master-buffer header-start header-end)
  3245.     (goto-char (point-min))
  3246.     (vm-reorder-message-headers nil nil
  3247.          "\\(Content-Type:\\|MIME-Version:\\|Content-Transfer-Encoding\\)")
  3248.     (insert "MIME-Version: 1.0\n")
  3249.     (insert (format
  3250.          (if vm-mime-avoid-folding-content-type
  3251.              "Content-Type: message/partial; id=%s; number=%d"
  3252.            "Content-Type: message/partial;\n\tid=%s;\n\tnumber=%d")
  3253.          id n))
  3254.     (if vm-mime-avoid-folding-content-type
  3255.         (insert (format "; total=" n))
  3256.       (insert (format ";\n\ttotal=" n)))
  3257.     (setq total-markers (cons (point) total-markers))
  3258.     (insert "\nContent-Transfer-Encoding: 7bit\n")
  3259.     (goto-char (point-max))
  3260.     (insert mail-header-separator "\n")
  3261.     (insert-buffer-substring master-buffer start end)
  3262.     (vm-increment n)
  3263.     (set-buffer master-buffer)
  3264.     (setq start (point)))
  3265.       (vm-decrement n)
  3266.       (vm-add-mail-mode-header-separator)
  3267.       (let ((bufs buffers))
  3268.     (while bufs
  3269.       (set-buffer (car bufs))
  3270.       (goto-char (car total-markers))
  3271.       (prin1 n (current-buffer))
  3272.       (setq bufs (cdr bufs)
  3273.         total-markers (cdr total-markers)))
  3274.     (set-buffer master-buffer))
  3275.       (message "Fragmenting message... done")
  3276.       (nreverse buffers))))
  3277.  
  3278. (defun vm-mime-preview-composition ()
  3279.   "Show how the current composition buffer might be displayed
  3280. in a MIME-aware mail reader.  VM copies and encodes the current
  3281. mail composition buffer and displays it as a mail folder.
  3282. Type `q' to quit this temp folder and return to composing your
  3283. message."
  3284.   (interactive)
  3285.   (if (not (eq major-mode 'mail-mode))
  3286.       (error "Command must be used in a VM Mail mode buffer."))
  3287.   (let ((temp-buffer nil)
  3288.     (mail-buffer (current-buffer))
  3289.     (enriched (and (boundp 'enriched-mode) enriched-mode))
  3290.     e-list)
  3291.     (unwind-protect
  3292.     (progn
  3293.       (setq temp-buffer (generate-new-buffer "composition preview"))
  3294.       (set-buffer temp-buffer)
  3295.       ;; so vm-mime-xxxx-encode-composition won't complain
  3296.       (setq major-mode 'mail-mode)
  3297.       (set (make-local-variable 'enriched-mode) enriched)
  3298.       (vm-insert-region-from-buffer mail-buffer)
  3299.       (goto-char (point-min))
  3300.       (or (vm-mail-mode-get-header-contents "From")
  3301.           (insert "From: " (user-login-name) "\n"))
  3302.       (or (vm-mail-mode-get-header-contents "Message-ID")
  3303.           (insert "Message-ID: <fake@fake.fake>\n"))
  3304.       (or (vm-mail-mode-get-header-contents "Date")
  3305.           (insert "Date: "
  3306.               (format-time-string "%a, %d %b %Y %H%M%S %Z"
  3307.                       (current-time))
  3308.               "\n"))
  3309.       (and vm-send-using-mime
  3310.            (null (vm-mail-mode-get-header-contents "MIME-Version:"))
  3311.            (vm-mime-encode-composition))
  3312.       (vm-remove-mail-mode-header-separator)
  3313.       (goto-char (point-min))
  3314.       (insert (vm-leading-message-separator 'From_))
  3315.       (goto-char (point-max))
  3316.       (insert (vm-trailing-message-separator 'From_))
  3317.       (set-buffer-modified-p nil)
  3318.       ;; point of no return, don't kill it if the user quits
  3319.       (setq temp-buffer nil)
  3320.       (let ((vm-auto-decode-mime-messages t)
  3321.         (vm-auto-displayed-mime-content-types t))
  3322.         (vm-save-buffer-excursion
  3323.          (vm-goto-new-folder-frame-maybe 'folder)
  3324.          (vm-mode)))
  3325.       (message
  3326.        (substitute-command-keys
  3327.         "Type \\[vm-quit] to continue composing your message"))
  3328.       ;; temp buffer, don't offer to save it.
  3329.       (setq buffer-offer-save nil)
  3330.       (vm-display (or vm-presentation-buffer (current-buffer)) t
  3331.               (list this-command) '(vm-mode startup)))
  3332.       (and temp-buffer (kill-buffer temp-buffer)))))
  3333.  
  3334. (defun vm-mime-composite-type-p (type)
  3335.   (or (and (vm-mime-types-match "message" type)
  3336.        (not (vm-mime-types-match "message/partial" type))
  3337.        (not (vm-mime-types-match "message/external-body" type)))
  3338.       (vm-mime-types-match "multipart" type)))
  3339.  
  3340. ;; Unused currrently.
  3341. ;;
  3342. ;;(defun vm-mime-map-atomic-layouts (function list)
  3343. ;;  (while list
  3344. ;;    (if (vm-mime-composite-type-p (car (vm-mm-layout-type (car list))))
  3345. ;;    (vm-mime-map-atomic-layouts function (vm-mm-layout-parts (car list)))
  3346. ;;      (funcall function (car list)))
  3347. ;;    (setq list (cdr list))))
  3348.  
  3349. (defun vm-mime-sprintf (format layout)
  3350.   ;; compile the format into an eval'able s-expression
  3351.   ;; if it hasn't been compiled already.
  3352.   (let ((match (assoc format vm-mime-compiled-format-alist)))
  3353.     (if (null match)
  3354.     (progn
  3355.       (vm-mime-compile-format format)
  3356.       (setq match (assoc format vm-mime-compiled-format-alist))))
  3357.     ;; The local variable name `vm-mime-layout' is mandatory here for
  3358.     ;; the format s-expression to work.
  3359.     (let ((vm-mime-layout layout))
  3360.       (eval (cdr match)))))
  3361.  
  3362. (defun vm-mime-compile-format (format)
  3363.   (let ((return-value (vm-mime-compile-format-1 format 0)))
  3364.     (setq vm-mime-compiled-format-alist
  3365.       (cons (cons format (nth 1 return-value))
  3366.         vm-mime-compiled-format-alist))))
  3367.  
  3368. (defun vm-mime-compile-format-1 (format start-index)
  3369.   (let ((case-fold-search nil)
  3370.     (done nil)
  3371.     (sexp nil)
  3372.     (sexp-fmt nil)
  3373.     (last-match-end start-index)
  3374.     new-match-end conv-spec)
  3375.     (store-match-data nil)
  3376.     (while (not done)
  3377.       (while
  3378.       (and (not done)
  3379.            (string-match
  3380.         "%\\(-\\)?\\([0-9]+\\)?\\(\\.\\(-?[0-9]+\\)\\)?\\([()acdefknNstT%]\\)"
  3381.         format last-match-end))
  3382.     (setq conv-spec (aref format (match-beginning 5)))
  3383.     (setq new-match-end (match-end 0))
  3384.     (if (memq conv-spec '(?\( ?a ?c ?d ?e ?f ?k ?n ?N ?s ?t ?T))
  3385.         (progn
  3386.           (cond ((= conv-spec ?\()
  3387.              (save-match-data
  3388.                (let ((retval (vm-mime-compile-format-1 format
  3389.                                    (match-end 5))))
  3390.              (setq sexp (cons (nth 1 retval) sexp)
  3391.                    new-match-end (car retval)))))
  3392.             ((= conv-spec ?a)
  3393.              (setq sexp (cons (list 'vm-mf-default-action
  3394.                         'vm-mime-layout) sexp)))
  3395.             ((= conv-spec ?c)
  3396.              (setq sexp (cons (list 'vm-mf-text-charset
  3397.                         'vm-mime-layout) sexp)))
  3398.             ((= conv-spec ?d)
  3399.              (setq sexp (cons (list 'vm-mf-content-description
  3400.                         'vm-mime-layout) sexp)))
  3401.             ((= conv-spec ?e)
  3402.              (setq sexp (cons (list 'vm-mf-content-transfer-encoding
  3403.                         'vm-mime-layout) sexp)))
  3404.             ((= conv-spec ?f)
  3405.              (setq sexp (cons (list 'vm-mf-attachment-file
  3406.                         'vm-mime-layout) sexp)))
  3407.             ((= conv-spec ?k)
  3408.              (setq sexp (cons (list 'vm-mf-event-for-default-action
  3409.                         'vm-mime-layout) sexp)))
  3410.             ((= conv-spec ?n)
  3411.              (setq sexp (cons (list 'vm-mf-parts-count
  3412.                         'vm-mime-layout) sexp)))
  3413.             ((= conv-spec ?N)
  3414.              (setq sexp (cons (list 'vm-mf-partial-number
  3415.                         'vm-mime-layout) sexp)))
  3416.             ((= conv-spec ?s)
  3417.              (setq sexp (cons (list 'vm-mf-parts-count-pluralizer
  3418.                         'vm-mime-layout) sexp)))
  3419.             ((= conv-spec ?t)
  3420.              (setq sexp (cons (list 'vm-mf-content-type
  3421.                         'vm-mime-layout) sexp)))
  3422.             ((= conv-spec ?T)
  3423.              (setq sexp (cons (list 'vm-mf-partial-total
  3424.                         'vm-mime-layout) sexp))))
  3425.           (cond (vm-display-using-mime
  3426.              (setcar sexp
  3427.                  (list 'vm-decode-mime-encoded-words-in-string
  3428.                    (car sexp)))))
  3429.           (cond ((and (match-beginning 1) (match-beginning 2))
  3430.              (setcar sexp
  3431.                  (list
  3432.                   (if (eq (aref format (match-beginning 2)) ?0)
  3433.                   'vm-numeric-left-justify-string
  3434.                 'vm-left-justify-string)
  3435.                   (car sexp)
  3436.                   (string-to-int
  3437.                    (substring format
  3438.                       (match-beginning 2)
  3439.                       (match-end 2))))))
  3440.             ((match-beginning 2)
  3441.              (setcar sexp
  3442.                  (list
  3443.                   (if (eq (aref format (match-beginning 2)) ?0)
  3444.                   'vm-numeric-right-justify-string
  3445.                 'vm-right-justify-string)
  3446.                   (car sexp)
  3447.                   (string-to-int
  3448.                    (substring format
  3449.                       (match-beginning 2)
  3450.                       (match-end 2)))))))
  3451.           (cond ((match-beginning 3)
  3452.              (setcar sexp
  3453.                  (list 'vm-truncate-string (car sexp)
  3454.                    (string-to-int
  3455.                     (substring format
  3456.                            (match-beginning 4)
  3457.                            (match-end 4)))))))
  3458.           (cond (vm-display-using-mime
  3459.              (setcar sexp
  3460.                  (list 'vm-reencode-mime-encoded-words-in-string
  3461.                    (car sexp)))))
  3462.           (setq sexp-fmt
  3463.             (cons "%s"
  3464.               (cons (substring format
  3465.                        last-match-end
  3466.                        (match-beginning 0))
  3467.                 sexp-fmt))))
  3468.       (setq sexp-fmt
  3469.         (cons (if (eq conv-spec ?\))
  3470.               (prog1 "" (setq done t))
  3471.             "%%")
  3472.               (cons (substring format
  3473.                        (or last-match-end 0)
  3474.                        (match-beginning 0))
  3475.                 sexp-fmt))))
  3476.     (setq last-match-end new-match-end))
  3477.       (if (not done)
  3478.       (setq sexp-fmt
  3479.         (cons (substring format last-match-end (length format))
  3480.               sexp-fmt)
  3481.         done t))
  3482.       (setq sexp-fmt (apply 'concat (nreverse sexp-fmt)))
  3483.       (if sexp
  3484.       (setq sexp (cons 'format (cons sexp-fmt (nreverse sexp))))
  3485.     (setq sexp sexp-fmt)))
  3486.     (list last-match-end sexp)))
  3487.  
  3488. (defun vm-mime-find-format-for-layout (layout)
  3489.   (let ((p vm-mime-button-format-alist)
  3490.     (type (car (vm-mm-layout-type layout))))
  3491.     (catch 'done
  3492.       (while p
  3493.     (if (vm-mime-types-match (car (car p)) type)
  3494.         (throw 'done (cdr (car p)))
  3495.       (setq p (cdr p))))
  3496.       "%-35.35t [%k to %a]" )))
  3497.  
  3498. (defun vm-mf-content-type (layout)
  3499.   (car (vm-mm-layout-type layout)))
  3500.  
  3501. (defun vm-mf-content-transfer-encoding (layout)
  3502.   (vm-mm-layout-encoding layout))
  3503.  
  3504. (defun vm-mf-content-description (layout)
  3505.   (or (vm-mm-layout-description layout)
  3506.       (let ((p vm-mime-type-description-alist)
  3507.         (type (car (vm-mm-layout-type layout))))
  3508.     (catch 'done
  3509.       (while p
  3510.         (if (vm-mime-types-match (car (car p)) type)
  3511.         (throw 'done (cdr (car p)))
  3512.           (setq p (cdr p))))
  3513.       nil ))
  3514.       (vm-mf-content-type layout)))
  3515.  
  3516. (defun vm-mf-text-charset (layout)
  3517.   (or (vm-mime-get-parameter layout "charset")
  3518.       "us-ascii"))
  3519.  
  3520. (defun vm-mf-parts-count (layout)
  3521.   (int-to-string (length (vm-mm-layout-parts layout))))
  3522.  
  3523. (defun vm-mf-parts-count-pluralizer (layout)
  3524.   (if (= 1 (length (vm-mm-layout-parts layout))) "" "s"))
  3525.  
  3526. (defun vm-mf-partial-number (layout)
  3527.   (or (vm-mime-get-parameter layout "number")
  3528.       "?"))
  3529.  
  3530. (defun vm-mf-partial-total (layout)
  3531.   (or (vm-mime-get-parameter layout "total")
  3532.       "?"))
  3533.  
  3534. (defun vm-mf-attachment-file (layout)
  3535.   (or (vm-mime-get-disposition-parameter layout "filename")
  3536.       (and (vm-mime-types-match "application" (car (vm-mm-layout-type layout)))
  3537.        (vm-mime-get-parameter layout "name"))
  3538.       "<no suggested filename>"))
  3539.  
  3540. (defun vm-mf-event-for-default-action (layout)
  3541.   (if (vm-mouse-support-possible-here-p)
  3542.       "Click mouse-2"
  3543.     "Press RETURN"))
  3544.  
  3545. (defun vm-mf-default-action (layout)
  3546.   (or vm-mf-default-action
  3547.       (let ((p vm-mime-default-action-string-alist)
  3548.         (type (car (vm-mm-layout-type layout))))
  3549.     (catch 'done
  3550.       (while p
  3551.         (if (vm-mime-types-match (car (car p)) type)
  3552.         (throw 'done (cdr (car p)))
  3553.           (setq p (cdr p))))
  3554.       nil ))
  3555.       ;; should not be reached
  3556.       "burn in the raging fires of hell forever"))
  3557.